【问题标题】:Why won't this code deadlock?为什么这段代码不会死锁?
【发布时间】:2016-01-21 03:17:20
【问题描述】:

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

【问题讨论】:

    标签: java swing deadlock


    【解决方案1】:

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

    使用 invokeLater 将更新插入 EDT 可确保在 setText 和下一次绘制操作之间存在 happens-before

    更新:由于您现在通过将Runnable 排队的位置移动成功地使代码死锁,问题是当您尝试排队时 EDT 尚未运行对其进行操作。

    【讨论】:

    • 为什么在this 的问题中他们在与我的类似设置中陷入僵局?它们有什么区别
    • @Aequitas 对于初学者,您没有在 Swing 对象(框架)上同步,然后在 Swing 事件线程启动后提交任务(另一个尝试提交它在任何 Swing 启动之前)。
    • 我明白了,谢谢,我将 invokeLater 移到了 main 而不是按下按钮,现在它死锁了。同步是什么意思?
    • 这太令人困惑了,我的死锁代码,如果我删除 setLayout 行它将不再死锁,为什么布局会改变任何东西?
    【解决方案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 是由两个线程并行构建的。

    【讨论】:

    • 请参阅this 问题,他们使用 invokeLater 进行了与我类似的设置,但仍然设法陷入僵局。为什么像你说的那样在一个线程上?
    • 更正式地说,死锁必须满足的条件是:互斥、保持等待、不抢占和循环等待。
    【解决方案3】:

    从不同的线程调用 Swing 方法确实是不明智的,但不是因为有死锁的风险。主要风险是:

    • 线程干扰
    • 内存一致性错误

    根据The Event Dispatch Thread。死锁主要源于多线程中不正确排序的锁定机制。由于许多 Swing 对象显然没有适当的锁定,因此死锁不是主要问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-08-20
      • 2012-08-27
      • 1970-01-01
      • 1970-01-01
      • 2021-12-10
      • 1970-01-01
      • 1970-01-01
      • 2021-06-20
      相关资源
      最近更新 更多