阻塞方法引起的死锁

时间:2015-10-31 14:45:36

标签: java multithreading

假设我们创建了一个运行同步方法的线程。此方法从空阻塞队列尝试take()。现在让一个单独的线程在同一个对象上同步时尝试put()和元素到阻塞队列。

这导致死锁:

  • 在将一个元素添加到队列之前,第一个线程不会释放锁。
  • 第二个线程无法添加元素,直到锁定空闲才能获取。

如果这两个动作需要是原子的并且在不同的线程上运行,那么如何在不造成死锁的情况下实现呢?

我了解take()put()是线程安全的。我的问题是当它们被用作必须是原子的大型行动的一部分时。

示例:

import java.util.concurrent.*;

public class DeadlockTest {

    String input = "Nothing added yet!";
    LinkedBlockingQueue<String> buffer = new LinkedBlockingQueue<>();

    public synchronized String getFromBuffer() {
        System.out.println("Trying to get input from buffer.");
        try {
            input = buffer.take();
        } catch (InterruptedException ex) {}
        System.out.println("Got:" + input + "\n");
        return input;
    }

    public static void main(String[] args) throws InterruptedException {
        DeadlockTest dl = new DeadlockTest();

        new Thread(() -> {
            dl.getFromBuffer();
        }).start();

        // Give new thread time to run.
        Thread.sleep(500);

        synchronized (dl) {
            String message = "Hello, world!";

            System.out.println("Adding: " + message);
            dl.buffer.put(message);
            System.out.println("Added!\n");

            System.out.println("Message: " + dl.input);
        }
    }
}

1 个答案:

答案 0 :(得分:3)

  

假设我们创建了一个运行同步方法的线程。此方法尝试从空的阻塞队列中获取()。

听起来很糟糕的设计。从synchronized方法或synchronized语句中调用任何阻止方法通常是错误的。

  

如果这两个动作需要是原子的并且在不同的线程上运行,那么如何在不造成死锁的情况下实现呢?

嗯,有两种可能性:

在一种情况下,两个线程对不同的数据起作用。在这种情况下,他们应该使用不同的锁,他们根本不会互相干扰。

在另一种情况下,两个线程作用于相同的数据。在这种情况下,他们应该锁定相同的锁,并且一个线程将不得不等待另一个。

也许您误解了阻塞队列的工作原理。如果一个线程正在等待来自阻塞队列的take()某个东西,那绝不应该阻止另一个线程调用put()。这与你想要的完全相反。

您想要的(以及您从Java标准库中的任何阻塞队列实现中获得的内容)是第二个线程中的put()操作将唤醒“#{1}}操作。等待队列中take()的东西。