Jak na to, když mám dvě vlákna?
Mám nějaký JPanel a potom nějakou TridaX.
Ve TridaX, je nějaký náročnější výpočet, který je potřeba dát do threadu. V této třídě mám nějaký parametr: double progress, ve kterém si zaznamenávám stav výpočtu (kolik % je již hotovo).
A na JPanelu by se měl vypisovat stav výpočtu v %. Takže jsem tam chtěl udělat další vlákno, které se bude dotazovat té třídy TridaX před její instanci a brat si hodnotu progress a pravidelně vypisovat hodnotu dokud nebude rovna 100%.
Ale nějak se mi nedaří ty vlákna vytvořit, aby to pracovalo správně. Nejspíše ty vlákna definuji nějak špatně. Poradil by mi tu někdo, jak to ty dvě vlákna udělat?
Vlákna jsou v Java dost jednoduchá záležitost.
1. nadefinuješ vlákno, viz Google.
2. vlákno si spočítá, jak dlouho mu to bude trvat a přírůstek si počítá samo (vlákno ví nejlépe, jestli je na začátku práce nebo v polovině)
3. pro výměnu dat můžeš použít zápis třeba někam do proměnné třídy
4. hlavní program si animuje průběh
U Threadpoolu, když ti na jedné úloze dělá třeba 10 vláken, to už je složitější.
Ve swingu to není tak jednoduché, protože ten je single-thread (není thread safe). Je několik možností buď pomocí
SwingUtilities.invokeLater(Runnable doRun);
nebo implementovat SwingWorker<T,V> To je třída přesně určená pro tyto případy.
Pokud není angličtina pro tebe problém přečti si tento článek http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html (http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html)
invokeLater zrejme nebude potrebny. Ta uloha vyzera presne na pouzitie SwingWorkera, ktory to vsetko zvladne. Za normalnych okolnosti by to bol pomerne nevesely problem spojeny s koordinaciou vlakien.
Svojho casu som napisal clanok k pouzitiu SwingWorkera http://ics.upjs.sk/~novotnyr/wiki/Java/SwingThreads
Áno, SwingWorker je štandardným riešením vášho problému. Upozorňujem, že SwingWorkerov bolo v minulosti v obehu viacero. Pokiaľ môžete, použite javax.swing.SwingWorker<T, V> z JRE (since 1.6). Ten ponúka API aj pre progress výpočtu.
V Swingu je dôležité manipulovať s GUI výhradne vo vlákne, ktoré sa zvykne označovať ako "event dispatch thread". To sa týka aj vytvorenia a zobrazenia vášho JPanelu a JFramu/JDialogu. Na to je dobrá tá metóda invokeLater.
Tento kód by vám mohol pomôcť:
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import javax.swing.SwingWorker;
import com.h311.swing.GridBagLayout;
public class Test {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
final JTextField myResultField = new JTextField();
final JProgressBar myProgress = new JProgressBar();
myProgress.setStringPainted(true);
final JButton myStartButton = new JButton("Start");
myStartButton.setActionCommand("start");
final JButton myCancelButton = new JButton("Cancel");
myCancelButton.setActionCommand("cancel");
myCancelButton.setEnabled(false);
ActionListener actionListener = new ActionListener() {
private SwingWorker<Integer, Integer> worker;
@Override
public void actionPerformed(ActionEvent e) {
if (worker != null) {
worker.cancel(true);
}
if ("start".equals(e.getActionCommand())) {
worker = new SwingWorker<Integer, Integer>(){
@Override
protected Integer doInBackground() throws Exception {
/* 5 seconds */
int durationInMilliseconds = 1000 * 5;
long startTime = System.currentTimeMillis();
long deadLine = startTime + durationInMilliseconds;
int result = 0;
while (!isCancelled()) {
/* do something meaningful */
long now = System.currentTimeMillis();
if (now > deadLine) {
break;
}
long timeLeft = now - startTime;
publish(Integer.valueOf((int) ((timeLeft / (double)durationInMilliseconds) * 100)));
result++;
}
return result;
}
@Override
protected void process(List<Integer> chunks) {
if (!isCancelled() && chunks != null && chunks.size() > 1) {
/* consider only the last value */
int value = chunks.get(chunks.size() -1);
myProgress.setValue(value);
myProgress.setString(new StringBuilder(3 + 2).append(value).append(' ').append('%').toString());
}
}
@Override
protected void done() {
try {
Integer result = get();
myResultField.setText(String.valueOf(result));
} catch (CancellationException e) {
myProgress.setString("Cancelled");
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
finally {
myStartButton.setEnabled(true);
myCancelButton.setEnabled(false);
}
}
};
myStartButton.setEnabled(false);
myCancelButton.setEnabled(true);
myResultField.setText("");
worker.execute();
}
}
};
myStartButton.addActionListener(actionListener);
myCancelButton.addActionListener(actionListener);
JPanel myPanel = new JPanel(new GridBagLayout());
int row = 0;
GridBagConstraints startConstraints = new GridBagConstraints();
startConstraints.gridx = 0;
startConstraints.gridy = row;
myPanel.add(myStartButton, startConstraints);
GridBagConstraints cancelConstraints = new GridBagConstraints();
cancelConstraints.gridx = 1;
cancelConstraints.gridy = row;
myPanel.add(myCancelButton, cancelConstraints);
row++;
GridBagConstraints progressConstraints = new GridBagConstraints();
progressConstraints.gridx = 0;
progressConstraints.gridy = row;
progressConstraints.fill = GridBagConstraints.HORIZONTAL;
progressConstraints.gridwidth = 2;
myPanel.add(myProgress, progressConstraints);
row++;
GridBagConstraints resultLabelConstraints = new GridBagConstraints();
resultLabelConstraints.gridx = 0;
resultLabelConstraints.gridy = row;
myPanel.add(new JLabel("Iterations"), resultLabelConstraints);
GridBagConstraints resultConstraints = new GridBagConstraints();
resultConstraints.gridx = 1;
resultConstraints.gridy = row;
resultConstraints.fill = GridBagConstraints.HORIZONTAL;
myPanel.add(myResultField, resultConstraints);
JFrame myFrame = new JFrame();
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setContentPane(myPanel);
myFrame.pack();
myFrame.setVisible(true);
}
});
}
}