Threads Multithreading Threads sollen warten

  • Themenstarter Themenstarter Bungeefan
  • Beginndatum Beginndatum
B

Bungeefan

Gast
Hey ich habe ein Probelm mit meinem Checksum Programm.
Es werden mit mehreren Threads die Checksummen der Dateien gleichzeitig berechnet.
Jetzt habe ich aber das Problem das er bestimmte Buttons in der GUI erst wieder enablen soll wenn ALLE fertig sind.
Ich hab schon was mit wait() und notify() probiert aber da blicke ich nicht ganz durch.
Wäre nett wenn mit jemand helfen könnte.
Java:
btnBrowse.setEnabled(false);
                    btnCopyMD2.setEnabled(false);
                    btnCopyMD5.setEnabled(false);
                    btnCopySHA_1.setEnabled(false);
                    btnCopySHA_256.setEnabled(false);
                    btnCopySHA_384.setEnabled(false);
                    btnCopySHA_512.setEnabled(false);
                    btnCopyAll.setEnabled(false);
                    btnVerify.setEnabled(false);
                    btnVerify.setEnabled(false);
                    chckbxMD2.setEnabled(false);
                    chckbxMD5.setEnabled(false);
                    chckbxSHA1.setEnabled(false);
                    chckbxSHA256.setEnabled(false);
                    chckbxSHA384.setEnabled(false);
                    chckbxSHA512.setEnabled(false);
                    try {
                        new Thread(new Runnable() {

                            @Override
                            public void run() {

                                if (chckbxMD2.isSelected()) {
                                    generateHash.add(barMD2);
                                    barMD2.setIndeterminate(true);
                                    generateHash.revalidate();
                                    generateHash.repaint();
                                    try {
                                        Checksum_Utility.instance.md2 = Checksum_Utility.instance.createMD2(datei);
//Berechnet Checksumme
                                    } catch (NoSuchAlgorithmException | IOException e) {
                                        e.printStackTrace();
                                    }
                                    barMD2.setIndeterminate(false);
                                    generateHash.remove(barMD2);
                                    generateHash.revalidate();
                                    generateHash.repaint();
                                    fieldMD2.setEnabled(true);
                                    fieldMD2.setText(Checksum_Utility.instance.md2);
                                    btnCopyMD2.setEnabled(true);
                                }
                                chckbxMD2.setEnabled(true);
                                this.notify();
                            }
                        }).start();
                        new Thread(new Runnable() {

                            @Override
                            public void run() {
                                if (chckbxMD5.isSelected()) {
                                    generateHash.add(barMD5);
                                    barMD5.setIndeterminate(true);
                                    try {
                                        Checksum_Utility.instance.md5 = Checksum_Utility.instance.createMD5(datei);
//Berechnet Checksumme
                                    } catch (NoSuchAlgorithmException | IOException e) {
                                        e.printStackTrace();
                                    }
                                    barMD5.setIndeterminate(false);
                                    generateHash.remove(barMD5);
                                    fieldMD5.setEnabled(true);
                                    fieldMD5.setText(Checksum_Utility.instance.md5);
                                    btnCopyMD5.setEnabled(true);
                                }
                                chckbxMD5.setEnabled(true);
                            }
                        }).start();
                        new Thread(new Runnable() {

                            @Override
                            public void run() {
                                if (chckbxSHA1.isSelected()) {
                                    generateHash.add(barSHA1);
                                    barSHA1.setIndeterminate(true);
                                    try {
                                        Checksum_Utility.instance.sha1 = Checksum_Utility.instance.createSHA1(datei);
//Berechnet Checksumme
                                    } catch (NoSuchAlgorithmException | IOException e) {
                                        e.printStackTrace();
                                    }
                                    barSHA1.setIndeterminate(false);
                                    generateHash.remove(barSHA1);
                                    fieldSHA1.setEnabled(true);
                                    fieldSHA1.setText(Checksum_Utility.instance.sha1);
                                    btnCopySHA_1.setEnabled(true);
                                }
                                chckbxSHA1.setEnabled(true);
                            }
                        }).start();
                        new Thread(new Runnable() {

                            @Override
                            public void run() {
                                if (chckbxSHA256.isSelected()) {
                                    generateHash.add(barSHA256);
                                    barSHA256.setIndeterminate(true);
                                    try {
                                        Checksum_Utility.instance.sha256 = Checksum_Utility.instance
                                                .createSHA256(datei);
//Berechnet Checksumme
                                    } catch (NoSuchAlgorithmException | IOException e) {
                                        e.printStackTrace();
                                    }
                                    barSHA256.setIndeterminate(false);
                                    generateHash.remove(barSHA256);
                                    fieldSHA256.setEnabled(true);
                                    fieldSHA256.setText(Checksum_Utility.instance.sha256);
                                    btnCopySHA_256.setEnabled(true);
                                }
                                chckbxSHA256.setEnabled(true);
                            }
                        }).start();
                        new Thread(new Runnable() {

                            @Override
                            public void run() {
                                if (chckbxSHA384.isSelected()) {
                                    generateHash.add(barSHA384);
                                    barSHA384.setIndeterminate(true);
                                    try {
                                        Checksum_Utility.instance.sha384 = Checksum_Utility.instance
                                                .createSHA384(datei);
//Berechnet Checksumme
                                    } catch (NoSuchAlgorithmException | IOException e) {
                                        e.printStackTrace();
                                    }
                                    barSHA384.setIndeterminate(false);
                                    generateHash.remove(barSHA384);
                                    fieldSHA384.setEnabled(true);
                                    fieldSHA384.setText(Checksum_Utility.instance.sha384);
                                    btnCopySHA_384.setEnabled(true);
                                }
                                chckbxSHA384.setEnabled(true);
                            }
                        }).start();
                        Thread th = new Thread(new Runnable() {

                            @Override
                            public void run() {
                                if (chckbxSHA512.isSelected()) {
                                    generateHash.add(barSHA512);
                                    barSHA512.setIndeterminate(true);
                                    try {
                                        Checksum_Utility.instance.sha512 = Checksum_Utility.instance
                                                .createSHA512(datei);
//Berechnet Checksumme
                                    } catch (NoSuchAlgorithmException | IOException e) {
                                        e.printStackTrace();
                                    }
                                    barSHA512.setIndeterminate(false);
                                    generateHash.remove(barSHA512);
                                    fieldSHA512.setEnabled(true);
                                    fieldSHA512.setText(Checksum_Utility.instance.sha512);
                                    btnCopySHA_512.setEnabled(true);
                                }
                                chckbxSHA512.setEnabled(true);
                            }
                        });
                        th.start();
                        Checksum_Utility.instance.wait();
//--------------------------------------------------------------
                        btnCopyAll.setEnabled(true);
                        btnBrowse.setEnabled(true);
                        btnVerify.setEnabled(true);
//--------------------------------------------------------------
//Das hier soll erst ausgeführt werden wenn ALLE Checksummen berechnet (und angezeigt werden)
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
 
Also der Code der einzelnen Threads ist sehr ähnlich. Das lässt sich bestimmt stark kürzen indem du eine Methode schreibst, die einen neuen Thread startet.
Anstelle von wait und notify würde ich einen CountDownLatch verwenden.
 
Du musst latch.await() in einem anderen Thread ausführen. Allerdings solltest du dann auch SwingUtilities.invokeLater() verwenden um Updates an deiner GUI im korrekten Thread auszuführen.
 
DrZoidberg schrieb bereits in seinem ersten Beitrag
Also der Code der einzelnen Threads ist sehr ähnlich.
Du solltest zunächst lernen, solche Code-Verdoppelungen zu vermeiden (egal ob du "Anfänger" oder "Amateur" bist).
Wenn du das gelernt hast, können wir ernsthaft über Threads sprechen.

Damit ich nicht missverstanden werde: Wir helfen dir natürlich gerne!
 
Zuletzt bearbeitet:
Könntest du bitte ein Codebeispiel dazu bringen wie ich das vermeiden kann, mir fällt dazu leider nichts ein. Grundsätzlich ist mir klar das das eine unschöne Lösung ist aber ich bisher keine andere hatte.
 
Zuletzt bearbeitet von einem Moderator:
Hier mal wie Swingworker und CompletableFutures zusammenarbeiten könnten.
Java:
import javax.swing.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.IntStream;

public class Test {

  public static void main(String... args) {
    MyUI.start();
  }
}

class MyUI {

  private MyUI() {
    init();
  }

  private void init() {
    JFrame frame = new JFrame("Thread-Test");
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.setLocationByPlatform(true);

    JPanel mainPanel = new JPanel();
    mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));

    JPanel boxPanel = new JPanel();
    JCheckBox[] checkBoxes = new JCheckBox[3];
    for(int i = 0; i < checkBoxes.length; i++) {
      checkBoxes[i] = new JCheckBox();
      boxPanel.add(checkBoxes[i]);
    }

    mainPanel.add(boxPanel);

    DefaultListModel<String> listModel = new DefaultListModel<>();
    JList<String> finishedList = new JList<>(listModel);

    JButton startCalculation = new JButton("Start");
    startCalculation.addActionListener(e -> {
      startCalculation.setEnabled(false);
      listModel.clear();
      SwingWorker<Void, String> mySwingWorker = new SwingWorker<Void, String>() {
        @Override
        protected Void doInBackground() throws Exception {
          CompletableFuture<?>[] collect = IntStream.range(0, checkBoxes.length).filter(i -> checkBoxes[i].isSelected())
            .mapToObj(i -> CompletableFuture.runAsync(Caluclations::calculate).thenAccept(x ->
              publish(Integer.toString(i))))
            .toArray(CompletableFuture<?>[]::new);
          CompletableFuture.allOf(collect).join();
          return null;
        }

        @Override
        protected void process(List<String> chunks) {
          chunks.forEach(listModel::addElement);
        }

        @Override
        protected void done() {
          startCalculation.setEnabled(true);
        }
      };
      mySwingWorker.execute();

    });

    mainPanel.add(finishedList);
    mainPanel.add(startCalculation);

    frame.add(mainPanel);

    frame.pack();
    frame.setVisible(true);
  }


  public static void start() {
    SwingUtilities.invokeLater(MyUI::new);
  }
}

class Caluclations {
  public static void calculate() {
    try {
      Thread.sleep(ThreadLocalRandom.current().nextInt(100, 5000));
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}
Soviele Häkchen gesetzt werden, so viele Threads werden gestartet und der Button wird disabled bis alle Berechnungen fertig sind.
 
Könntest du bitte ein Codebeispiel dazu bringen wie ich das vermeiden kann, mir fällt dazu leider nichts ein. Grundsätzlich ist mir klar das das eine unschöne Lösung ist aber ich bisher keine andere hatte.

Du kannst die ganzen graphischen Komponenten in Objekt bzw. Arrays stecken, um sie in einer Schleife abarbeiten zu können.
Das könnte dann z.B. so aussehen. Ich habe hier auch einen CountDownLatch verwendet, um solange zu warten bis alle Threads fertig sind.
Java:
import java.awt.GridLayout;
import java.io.File;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Stream;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

public class T {
  static class UiGroup {
    String algorithm, hash = "";
    JButton button;
    JCheckBox checkbox;
    JProgressBar bar;
    JTextField field;
  }
  private static String[] algorithms = {"MD2", "MD5", "SHA_1", "SHA_256", "SHA_384", "SHA_512"};
  private static UiGroup[] uiGroups = new UiGroup[algorithms.length];
  private static File datei = new File("");

  static void createUI() {
    for(int i = 0; i < algorithms.length; i++) {
      uiGroups[i] = new UiGroup();
      uiGroups[i].algorithm = algorithms[i];
    }
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.setLayout(new GridLayout(algorithms.length + 1, 4, 10, 10));
    for(UiGroup uiGroup: uiGroups) {
      uiGroup.button = new JButton(uiGroup.algorithm);
      uiGroup.field = new JTextField();
      uiGroup.bar = new JProgressBar();
      uiGroup.checkbox = new JCheckBox();
      frame.add(uiGroup.button);
      frame.add(uiGroup.field);
      frame.add(uiGroup.bar);
      frame.add(uiGroup.checkbox);
    }
    JButton startBtn = new JButton("start");
    startBtn.addActionListener(e -> start());
    frame.add(startBtn);
    frame.pack();
    frame.setVisible(true);
  }

  private static String createHash(String algorithm, File Datei) {
    try {
      Thread.sleep(2000);
    } catch(InterruptedException e) {}
      return "abc";
    /*
    try {
      switch(algorithm) {
        case "MD2": return Checksum_Utility.instance.createMD2(datei);
        case "MD5": return Checksum_Utility.instance.createMD5(datei);
        case ...
      }
    } catch (NoSuchAlgorithmException | IOException e) {
        e.printStackTrace();
    }
    */
  }

  private static void awaitLatch(CountDownLatch latch) {
    try {
      latch.await();
    } catch(InterruptedException e) {
      awaitLatch(latch);
    }
  }

  private static void start() {
    CountDownLatch latch = new CountDownLatch(uiGroups.length);
    for(UiGroup uiGroup: uiGroups) {
      uiGroup.button.setEnabled(false);
      uiGroup.checkbox.setEnabled(false);
      if (uiGroup.checkbox.isSelected()) {
        new Thread(() -> {
          String hash = createHash(uiGroup.algorithm, datei);
          SwingUtilities.invokeLater(() -> uiGroup.hash = hash);
          latch.countDown();
        }).start();
      } else {
        latch.countDown();
      }
    }
    new Thread(() -> {
      awaitLatch(latch);
      SwingUtilities.invokeLater(() -> {
        for(UiGroup uiGroup: uiGroups) {
          uiGroup.button.setEnabled(true);
          uiGroup.checkbox.setEnabled(true);
          uiGroup.field.setText(uiGroup.hash);
        }
      });
    }).start();
  }

  public static void main(String[] args) {
    createUI();
  }
}

Anstatt Threads manuell zu verwalten, kann man aber auch parallele Streams verwenden. In dem Fall würde die start Methode so aussehen.
Java:
private static void start() {
  for(UiGroup uiGroup: uiGroups) {
    uiGroup.button.setEnabled(false);
    uiGroup.checkbox.setEnabled(false);
  }
  new Thread(() -> {
    UiGroup[] result = Stream.of(uiGroups)
          .filter(uiGroup -> uiGroup.checkbox.isSelected())
          .parallel()
          .map(uiGroup -> {
            uiGroup.hash = createHash(uiGroup.algorithm, datei);
            return uiGroup;
          })
          .toArray(n -> new UiGroup[n]);
    SwingUtilities.invokeLater(() -> {
      for(UiGroup uiGroup: result) {
        uiGroup.button.setEnabled(true);
        uiGroup.checkbox.setEnabled(true);
        uiGroup.field.setText(uiGroup.hash);
      }
    });
  }).start();
}
 
Ich habs jetzt mal so gelöst das ich alle "neuen" Threads wiederum in einen Thread packe, der dann auf die Threads mit dem Countdown wartet und dann die Buttons aktiviert. So wird die GUI nicht eingefroren aber es wird trotzdem gewartet.

Du kannst die ganzen graphischen Komponenten in Objekt bzw. Arrays stecken, um sie in einer Schleife abarbeiten zu können.
Das könnte dann z.B. so aussehen. Ich habe hier auch einen CountDownLatch verwendet, um solange zu warten bis alle Threads fertig sind.
Java:
import java.awt.GridLayout;
import java.io.File;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Stream;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

public class T {
  static class UiGroup {
    String algorithm, hash = "";
    JButton button;
    JCheckBox checkbox;
    JProgressBar bar;
    JTextField field;
  }
  private static String[] algorithms = {"MD2", "MD5", "SHA_1", "SHA_256", "SHA_384", "SHA_512"};
  private static UiGroup[] uiGroups = new UiGroup[algorithms.length];
  private static File datei = new File("");

  static void createUI() {
    for(int i = 0; i < algorithms.length; i++) {
      uiGroups[i] = new UiGroup();
      uiGroups[i].algorithm = algorithms[i];
    }
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.setLayout(new GridLayout(algorithms.length + 1, 4, 10, 10));
    for(UiGroup uiGroup: uiGroups) {
      uiGroup.button = new JButton(uiGroup.algorithm);
      uiGroup.field = new JTextField();
      uiGroup.bar = new JProgressBar();
      uiGroup.checkbox = new JCheckBox();
      frame.add(uiGroup.button);
      frame.add(uiGroup.field);
      frame.add(uiGroup.bar);
      frame.add(uiGroup.checkbox);
    }
    JButton startBtn = new JButton("start");
    startBtn.addActionListener(e -> start());
    frame.add(startBtn);
    frame.pack();
    frame.setVisible(true);
  }

  private static String createHash(String algorithm, File Datei) {
    try {
      Thread.sleep(2000);
    } catch(InterruptedException e) {}
      return "abc";
    /*
    try {
      switch(algorithm) {
        case "MD2": return Checksum_Utility.instance.createMD2(datei);
        case "MD5": return Checksum_Utility.instance.createMD5(datei);
        case ...
      }
    } catch (NoSuchAlgorithmException | IOException e) {
        e.printStackTrace();
    }
    */
  }

  private static void awaitLatch(CountDownLatch latch) {
    try {
      latch.await();
    } catch(InterruptedException e) {
      awaitLatch(latch);
    }
  }

  private static void start() {
    CountDownLatch latch = new CountDownLatch(uiGroups.length);
    for(UiGroup uiGroup: uiGroups) {
      uiGroup.button.setEnabled(false);
      uiGroup.checkbox.setEnabled(false);
      if (uiGroup.checkbox.isSelected()) {
        new Thread(() -> {
          String hash = createHash(uiGroup.algorithm, datei);
          SwingUtilities.invokeLater(() -> uiGroup.hash = hash);
          latch.countDown();
        }).start();
      } else {
        latch.countDown();
      }
    }
    new Thread(() -> {
      awaitLatch(latch);
      SwingUtilities.invokeLater(() -> {
        for(UiGroup uiGroup: uiGroups) {
          uiGroup.button.setEnabled(true);
          uiGroup.checkbox.setEnabled(true);
          uiGroup.field.setText(uiGroup.hash);
        }
      });
    }).start();
  }

  public static void main(String[] args) {
    createUI();
  }
}

Anstatt Threads manuell zu verwalten, kann man aber auch parallele Streams verwenden. In dem Fall würde die start Methode so aussehen.
Java:
private static void start() {
  for(UiGroup uiGroup: uiGroups) {
    uiGroup.button.setEnabled(false);
    uiGroup.checkbox.setEnabled(false);
  }
  new Thread(() -> {
    UiGroup[] result = Stream.of(uiGroups)
          .filter(uiGroup -> uiGroup.checkbox.isSelected())
          .parallel()
          .map(uiGroup -> {
            uiGroup.hash = createHash(uiGroup.algorithm, datei);
            return uiGroup;
          })
          .toArray(n -> new UiGroup[n]);
    SwingUtilities.invokeLater(() -> {
      for(UiGroup uiGroup: result) {
        uiGroup.button.setEnabled(true);
        uiGroup.checkbox.setEnabled(true);
        uiGroup.field.setText(uiGroup.hash);
      }
    });
  }).start();
}

Das Problem ist nur ich habe pro Thread mehrere verschiedene Komponenten die abwechselnd angesteuert werden d.h. ich bräuchte mehrere Arrays. Werde ich vielleicht mal machen, trotzdem danke für das Beispiel und eure Hilfe.
 

Neue Themen


Zurück
Oben