我正在研究setText上的死锁问题,但我需要先了解并了解死锁。为此,我创建了一个简短的程序来尝试复制可能在更大规模上发生的事情,但我不确定为什么我的小程序永远不会死锁。
这是我的学习计划:
public static void main(String[] a)
{
JFrame frame = new JFrame();
final JTextField p = new JTextField("start");
JButton btn = new JButton("button");
btn.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
SwingUtilities.invokeLater(new Runnable(){
@Override
public void run(){
p.setText(String.valueOf(System.nanoTime()));
}
});
}
});
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(p);
frame.getContentPane().add(btn);
frame.setSize(400, 400);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
我认为对swing的修改不能在单独的线程中完成,所以我有一个setText来在invokeLater
的按钮点击中更改JTextField。这样做应该打破单线程规则,这不会导致死锁吗?
答案 0 :(得分:2)
从其他线程更改Swing组件不会导致死锁您的程序(至少通常不会) - 只是JVM没有义务反映在其他线程中的一个线程中对状态所做的更改,除非发生在关系之前,例如synchronized
阻止或访问volatile
字段。 JVM可能决定一次读取变量的值,并且永远不会在当前线程中重新读取它,这意味着您的更新永远不会被绘制UI的线程看到,或者它可能会绕过以后在某些不可预知的时间更新它。
使用invokeLater
将更新插入EDT可确保在setText
和下一次绘制操作之间发生之前。
更新:您现在已经成功通过移动Runnable
排队的位置来使代码死锁,问题是EDT还没有运行当你试图对其进行排队时。
答案 1 :(得分:2)
在上面的示例中,您使用的是单个线程。与大多数GUI环境一样,Swing使用事件队列进行操作。此队列包含必须处理的内容,例如单击事件,文本框编辑事件。这些都在所谓的GUI线程上执行。 Swing不断重新绘制场景并处理队列中的事件。事件仅在一个线程上处理,这就是当您在单击处理程序中进行长计算(或网络)时应用程序冻结的原因。当您致电SwingUtilities.invokeLater
时,您的代码将被提交并放入事件队列中。当Swing有一些时间时,它会在GUI线程上执行它。
对于死锁,您需要满足以下条件:
潜在死锁的示例:
Thread1: Thread2:
lock(A) lock(B)
lock(B) lock(A) <---- may deadlock here
do stuff do stuff
free(B) free(A)
free(A) free(B)
您的示例和您在注释中链接的示例的主要区别在于,您在主线程上创建GUI(与其他示例类似),但在用户单击之前您不会调用Swing的GUI线程在按钮上。 GUI构建在主线程上,不会干扰Swing线程。在另一个例子中,GUI是从两个线程并行构建的。
答案 2 :(得分:0)
从不同的线程调用Swing方法确实不明智,但不是因为死锁的风险。主要风险是:
根据The Event Dispatch Thread。死锁主要源于多线程中不正确排序的锁定机制。由于许多Swing对象显然没有正确的锁定,因此死锁不是主要问题。