Java 回转中的螺纹和JLabel-工作不正常
请看下面的代码Java 回转中的螺纹和JLabel-工作不正常,java,multithreading,swing,jbutton,jlabel,Java,Multithreading,Swing,Jbutton,Jlabel,请看下面的代码 import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JFrame; import javax.swing.JPanel; class thread_jishu extends Thread{ @Override public void run(){ int p=-1; for(;;){ //here contin
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JFrame;
import javax.swing.JPanel;
class thread_jishu extends Thread{
@Override
public void run(){
int p=-1;
for(;;){
//here continuously checking that whether
//the value of label_thread.i is equals to p or not
if(label_thread.i!=p){
try{
Thread.sleep(1000);
}catch(Exception e){}
label_thread.lb.setText("after sleeping at -> "+label_thread.i);
// here i want to set the JLabel
// text after waiting 1sec only when
// label_thread.i has been changed,
// but not happening
p=label_thread.i;
}
}
}
}
public class label_thread implements java.awt.event.ActionListener{
/**
* @param evt
*/
@Override
public void actionPerformed(java.awt.event.ActionEvent evt){
i+=1;
lb.setText("Button clicked at -> "+i);
}
static int i=-1;
static JLabel lb=new JLabel("hello here");
static JFrame window=new JFrame("Jishu");
static JFrame window2=new JFrame("Jishu");
public static void main(String[] args) {
// TODO code application logic here
new thread_jishu().start();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setMinimumSize(new java.awt.Dimension(200,200));
JButton bt=new JButton("Click here");
bt.addActionListener(new label_thread());
JPanel panel=new JPanel();
panel.add(bt);
window.add(panel);
window2.add(lb);
window.setVisible(true);
window2.setVisible(true);
window2.setMinimumSize(new java.awt.Dimension(200,200));
}
}
当I
的值不等于p
时,我想在第二个窗口中重置JLabel
,这意味着在第一个窗口中单击了按钮
但是,当单击按钮时,标签的文本不会更改。不应在外部修改Swing元素。如果希望在AWT事件线程中执行代码,可以使用 比如:
SwingUtilities.invokeLater(new Runnable() {
@override
public void run() {
//Swing stuff here.
}
});
会让你非常接近。有许多潜在的问题
- 过度使用/依赖
静态
- 摆动线程冲突
- 变量的线程内存访问
- 而且总体设计很差
static
没有帮助,从长远来看可能会导致各种问题static
不是一种跨对象通信机制,您应该学习在没有它的情况下处理的其他技术
Swing是单线程框架,它不是线程安全的,这基本上意味着您不应该在事件调度线程的上下文中执行长时间运行的任务,也不应该从EDT上下文之外修改UI的状态(比如从另一个线程设置标签的文本)
从线程
s中访问变量可能会有问题,因为线程
可以获得自己的变量副本,这意味着线程之间对变量的读写可能会延迟,这意味着它们可能看不到最新的值。通常,您可以使用volatile
关键字或使用原子API(如AtomicInteger
)来解决这个问题,但我认为,如果设计得稍微好一点,就可以避免这些需求
现在,您可以使用SwingWorker
或Swing计时器
,这两种方法都提供了以保存方式控制EDT更新的解决方案,但您仍然使用线程
您的程序正遭受着基本的糟糕设计,您将一堆属性暴露于不受控制的修改和访问中,这使得解决类的责任(谁可以做什么,什么时候做)变得困难
首先,我要定义一些控件
public interface Counter {
public int getCount();
public void setText(String text);
}
这个简单的接口提供了对当前count
值的访问,并为另一个类提供了设置实现文本的方法。这就是“合同”的定义。这意味着,无论我将此接口的实例传递给谁,他们都只能执行这两项任务,而如何控制这些操作则取决于实现
接下来,我设置线程
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();
}
}
}
因此,这与您所做的没有什么不同,只是,我依靠计数器的实现来完成我需要它完成的工作
最后,实现计数器
,计数器
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);
}
});
}
}
}
这为用户提供了界面,并定义了计数器的工作方式。在setText
方法中,我们有一个安全防护装置,确保对JLabel
的所有修改都是在EDT的上下文中完成的,为了简单起见,JButton
的ActionListener
实际上也使用了setText
方法
可运行的示例
并发性从一开始就是一个复杂的主题,Swing API(与大多数GUI API一样)的需求使得并发性变得更加复杂
看看:
有关常见问题的更多详细信息和可能的解决方案
Swing计时器
示例。。。
以及一个使用Swing定时器的简单实现,不需要线程
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);
}
}
这可能是因为在项目中使用了相同的变量,所以会发生concurreny。尝试将变量i设置为label_线程类中的volatile,Swing组件只能在AWT事件调度线程中修改。在任何其他线程中修改它们将导致不可预测的行为。此外,从多个线程访问的变量,如label\u thread.i
,需要标记为volatile
。(您的try/catch应该在循环的周围,而不是循环的内部。)
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);
}
}