Reputation: 386
I'm trying to learn how to use threads. I have made an example using the API and documents Trying to make it as simple as possible . A form 2 buttons and a progress-bar to update I'm unable to get it to work from the API example. First My basic Form then the class that supposed to run the thread properly. When I click a button it freezes the GUI and doesn't update the progress bar, some mentioned I'm block the GUI thread... which confused me. Can someone tell me where I'm going wrong.
package mythreadtry;
/**
*
* @author brett
*/
public class MyThreadMainGui extends javax.swing.JDialog {
/**
* Creates new form MyThreadMainGui
*/
public MyThreadMainGui(java.awt.Frame parent, boolean modal) {
super(parent, modal);
initComponents();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
jButton1 = new javax.swing.JButton();
jButton2 = new javax.swing.JButton();
jProgressBar1 = new javax.swing.JProgressBar();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
jButton1.setText("jButton1");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
jButton2.setText("jButton2");
jButton2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton2ActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(36, 36, 36)
.addComponent(jButton1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jButton2)
.addContainerGap(208, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jProgressBar1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(210, Short.MAX_VALUE)
.addComponent(jProgressBar1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jButton1)
.addComponent(jButton2))
.addGap(35, 35, 35))
);
pack();
}// </editor-fold>
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
MyThreadTry mmt = new MyThreadTry();
mmt.doit(null);
}
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
MyThreadTry mmt = new MyThreadTry();
mmt.doit(null);
}
//
public void updateProgress(int newValue){
jProgressBar1.setValue(newValue);
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(MyThreadMainGui.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(MyThreadMainGui.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(MyThreadMainGui.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(MyThreadMainGui.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
/* Create and display the dialog */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
MyThreadMainGui dialog = new MyThreadMainGui(new javax.swing.JFrame(), true);
dialog.addWindowListener(new java.awt.event.WindowAdapter() {
@Override
public void windowClosing(java.awt.event.WindowEvent e) {
System.exit(0);
}
});
dialog.setVisible(true);
}
});
}
// Variables declaration - do not modify
private javax.swing.JButton jButton1;
private javax.swing.JButton jButton2;
private javax.swing.JProgressBar jProgressBar1;
// End of variables declaration
}
Next is my attempt at a thread.. which is simple I hope. I included the whole form so you guys can tell how im trying to call the class.
package mythreadtry;
import java.awt.event.MouseEvent;
/**
*
* @author brett
*/
public class MyThreadTry implements Runnable{
long minPrime;
volatile Thread p = new Thread();
volatile boolean threadSuspended;
MyThreadTry() {
int times = 100000;
for(int i = 0; i <= times; i++ ){
this.minPrime = i;
System.out.println(this.minPrime);
MyThreadMainGui myg = new MyThreadMainGui(null, threadSuspended);
myg.updateProgress((int)this.minPrime);
}
}
public void doit(String[] args) {
p.start();
}
public void run() {
Thread thisThread = Thread.currentThread();
threadSuspended = true;
while (p == thisThread) {
try {
p.sleep(500);
synchronized(this) {
while (threadSuspended && p==thisThread)
wait();
}
} catch (InterruptedException e){
}
}
}
public synchronized void mousePressed(MouseEvent e) {
e.consume();
threadSuspended = !threadSuspended;
if (!threadSuspended)
notify();
}
public synchronized void stop() {
this.p = null;
notify();
}
/**
* @param args the command line arguments
*/
}
Upvotes: 0
Views: 80
Reputation: 347314
When I click a button it freezes the GUI and doesn't update the progress bar
When you push one of your buttons, you create an instance of MyThreadTry
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
MyThreadTry mmt = new MyThreadTry();
mmt.doit(null);
}
The constructor of MyThreadTry
then loops 100, 000 times, creating 100, 000 instances of MyThreadMainGUI
MyThreadTry() {
int times = 100000;
for(int i = 0; i <= times; i++ ){
this.minPrime = i;
System.out.println(this.minPrime);
MyThreadMainGui myg = new MyThreadMainGui(null, threadSuspended);
myg.updateProgress((int)this.minPrime);
}
}
All within the context of the Event Dispatching Thread. The EDT is responsible for processing the Event Queue, which includes, amongst other things, paint events.
This means that until this method actually finishes, nothing can be updated.
See Concurrency in Swing for more details.
When the constructor "finally" returns, you call doit
, which calls p.start
. But since you've not overridden p
's run
method nor supplied it a Runnable
, it does nothing...
Updated with runnable example
My preferences is to use a SwingWorker
in these cases, but since we're talking about threads, here's a threaded version instead.
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JProgressBar pb;
private JButton button;
private Worker worker;
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
pb = new JProgressBar();
button = new JButton("Run");
add(pb, gbc);
add(button, gbc);
pb.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
System.out.println(pb.getValue());
if (pb.getValue() >= 100) {
button.setText("Run");
worker = null;
}
}
});
pb.addPropertyChangeListener("value", new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
}
});
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (worker == null) {
worker = new Worker(pb);
Thread t = new Thread(worker);
t.start();
button.setText("Pause");
} else {
if (worker.isPaused()) {
button.setText("Pause");
worker.resume();
} else {
button.setText("Resume");
worker.pause();
}
}
}
});
}
}
public class Worker implements Runnable {
private ReentrantLock pausedLock;
private Condition pausedCondition;
private AtomicBoolean paused;
private JProgressBar pb;
public Worker(JProgressBar pb) {
paused = new AtomicBoolean(false);
pausedLock = new ReentrantLock();
pausedCondition = pausedLock.newCondition();
this.pb = pb;
}
public void pause() {
paused.set(true);
}
public void resume() {
paused.set(false);
pausedLock.lock();
try {
pausedCondition.signal();
} finally {
pausedLock.unlock();
}
}
@Override
public void run() {
int times = 100_000;
for (int i = 0; i <= times; i++) {
checkPauseState();
updateProgress(Math.round((i / (float) times) * 100f));
try {
Thread.sleep(1);
} catch (InterruptedException ex) {
}
}
}
protected void checkPauseState() {
while (paused.get()) {
pausedLock.lock();
try {
pausedCondition.await();
} catch (InterruptedException ex) {
} finally {
pausedLock.unlock();
}
}
}
protected void updateProgress(int progress) {
if (EventQueue.isDispatchThread()) {
pb.setValue(progress);
} else {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
updateProgress(progress);
}
});
}
}
public boolean isPaused() {
return paused.get();
}
}
}
Make sure you make the time to read through Concurrency in Java, especially the section on locks, and Concurrency in Swing.
Upvotes: 3