为什么这个代码不会死锁?

时间:2016-01-21 03:17:20

标签: java swing deadlock

我正在研究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。这样做应该打破单线程规则,这不会导致死锁吗?

3 个答案:

答案 0 :(得分:2)

从其他线程更改Swing组件不会导致死锁您的程序(至少通常不会) - 只是JVM没有义务反映在其他线程中的一个线程中对状态所做的更改,除非发生在关系之前,例如synchronized阻止或访问volatile字段。 JVM可能决定一次读取变量的值,并且永远不会在当前线程中重新读取它,这意味着您的更新永远不会被绘制UI的线程看到,或者它可能会绕过以后在某些不可预知的时间更新它。

使用invokeLater将更新插入EDT可确保在setText和下一次绘制操作之间发生之前

更新:您现在已经成功通过移动Runnable排队的位置来使代码死锁,问题是EDT还没有运行当你试图对其进行排队时。

答案 1 :(得分:2)

在上面的示例中,您使用的是单个线程。与大多数GUI环境一样,Swing使用事件队列进行操作。此队列包含必须处理的内容,例如单击事件,文本框编辑事件。这些都在所谓的GUI线程上执行。 Swing不断重新绘制场景并处理队列中的事件。事件仅在一个线程上处理,这就是当您在单击处理程序中进行长计算(或网络)时应用程序冻结的原因。当您致电SwingUtilities.invokeLater时,您的代码将被提交并放入事件队列中。当Swing有一些时间时,它会在GUI线程上执行它。

对于死锁,您需要满足以下条件:

  • 至少两个主题
  • 通过首先锁定资源A和B来操作
  • 锁定在不同的线程上以不同的顺序发生

潜在死锁的示例:

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对象显然没有正确的锁定,因此死锁不是主要问题。