There are a number of potential issues;
- Overuse/reliance on
static
- Swing thread violations
- Thread memory access of variables
- And general poor design.
static
is not helpful and can cause all sorts of issues in the long run. static
is not a cross object communication mechanism and you should learn other techniques for dealing without it
Swing is single threaded framework and it is not thread safe, this basically means that you should not perform long running tasks within the context of the Event Dispatching Thread and you should also not modify the state of the UI from outside the context of the EDT (like setting the text of the label from another thread).
Accessing variables from within Thread
s and be problematic, as the Thread
can get it's own copy of the variable, this means that reads and writes to the variable can be delayed between threads, meaning that they may not see the latest value. Typically, you can resolve this with the volatile
keyword or using the atomic API, like AtomicInteger
, but I think with a little better design, you can avoid these needs.
Now, you could use a SwingWorker
or a Swing Timer
, both of which provide solutions to controlling updates to the EDT in a save way, but you also still use a thread.
You program is suffering from basic poor design, you're expose a bunch of properties to uncontrolled modifications and access, making it difficult to resolve the responsibilities of the classes (who can do what and when).
So, to start with, I'm going to define some control
public interface Counter {
public int getCount();
public void setText(String text);
}
This simple interface provides access to the current count
value and provides a means for another class to set the text of the implementation. This defines the "contract". This means that who ever I pass an instance of this interface to, they can only perform those two tasks and it's up to the implementation to decide how those actions are controlled
Next, I setup the Thread
....
public class ThreadJishu implements Runnable {
private Counter counter;
private int initialCount;
public ThreadJishu(Counter counter) {
this.counter = counter;
}
@Override
public void run() {
this.initialCount = counter.getCount();
while (true) {
if (counter.getCount() != initialCount) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
initialCount = counter.getCount();
counter.setText("After sleep at -> " + initialCount);
}
Thread.yield();
}
}
}
So, this isn't that different from what you were doing, except, I'm relying on the implementation of Counter
to do the work I need it to do.
And finally, the implementation of Counter
, the CounterPane
public class CounterPane extends JPanel implements Counter {
private int count = 0;
private JLabel label;
public CounterPane() {
label = new JLabel("Hello");
JButton btn = new JButton("Click here");
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(label, gbc);
add(btn, gbc);
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
count++;
setText("Button click count = " + count);
}
});
Thread t = new Thread(new ThreadJishu(this));
t.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
public int getCount() {
return count;
}
@Override
public void setText(String text) {
if (EventQueue.isDispatchThread()) {
label.setText(text);
} else {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
setText(text);
}
});
}
}
}
This provides the interface to the user as well as defines the workings of the Counter
. In the setText
method, we have a safe guard which ensures that all modifications to the JLabel
are done from within the context of the EDT and for simplicity, the JButton
's ActionListener
actually uses the setText
method as well.
Runnable example...

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class LabelThread {
public static void main(String[] args) {
new LabelThread();
}
public LabelThread() {
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 CounterPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface Counter {
public int getCount();
public void setText(String text);
}
public class CounterPane extends JPanel implements Counter {
private int count = 0;
private JLabel label;
public CounterPane() {
label = new JLabel("Hello");
JButton btn = new JButton("Click here");
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(label, gbc);
add(btn, gbc);
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
count++;
setText("Button click count = " + count);
}
});
Thread t = new Thread(new ThreadJishu(this));
t.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
public int getCount() {
return count;
}
@Override
public void setText(String text) {
if (EventQueue.isDispatchThread()) {
label.setText(text);
} else {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
setText(text);
}
});
}
}
}
public class ThreadJishu implements Runnable {
private Counter counter;
private int initialCount;
public ThreadJishu(Counter counter) {
this.counter = counter;
}
@Override
public void run() {
this.initialCount = counter.getCount();
while (true) {
if (counter.getCount() != initialCount) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
initialCount = counter.getCount();
counter.setText("After sleep at -> " + initialCount);
}
Thread.yield();
}
}
}
}
Concurrency is a complex subject to begin with, made more complicated by the needs of the Swing API (like most GUI APIs).
Have a look at:
for more details and possible solutions for common problems
Swing Timer
example...
And a simple implementation using a Swing Timer
, no Thread
's required
public class CounterPane extends JPanel implements Counter {
private int count = 0;
private JLabel label;
private Timer timer;
public CounterPane() {
label = new JLabel("Hello");
JButton btn = new JButton("Click here");
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(label, gbc);
add(btn, gbc);
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
count++;
setText("Button click count = " + count);
timer.restart();
}
});
timer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setText("After sleep at -> " + getCount());
}
});
timer.setRepeats(false);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
public int getCount() {
return count;
}
@Override
public void setText(String text) {
label.setText(text);
}
}