Core Java Tutorial -- Deadlock

Java 中的死锁是一种编程情况,两个或更多的线程永远被阻塞。至少有两个线程和两个或更多资源出现 Java 死锁情况。在这里我写了一个简单的程序,它会导致 Java 死锁情况,然后我们将看到如何分析它。

让我们来看看一个简单的程序,我将在 Java 线程中创建死锁。

package Thread;

public class ThreadDeadlock {
    public static void main(String[] args) throws InterruptedException {
        Object obj1 = new Object();
        Object obj2 = new Object();
        Object obj3 = new Object();

        Thread t1 = new Thread(new SyncThread(obj1, obj2), "t1");
        Thread t2 = new Thread(new SyncThread(obj2, obj3), "t2");
        Thread t3 = new Thread(new SyncThread(obj3, obj1), "t3");

        t1.start();
        Thread.sleep(5000);
        t2.start();
        Thread.sleep(5000);
        t3.start();

    }

}

class SyncThread implements Runnable {
    private Object obj1;
    private Object obj2;

    public SyncThread(Object o1, Object o2) {
        this.obj1 = o1;
        this.obj2 = o2;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name + " acquiring lock on " + obj1);
        synchronized (obj1) {
            System.out.println(name + " acquired lock on " + obj1);
            work();
            System.out.println(name + " acquiring lock on " + obj2);
            synchronized (obj2) {
                System.out.println(name + " acquired lock on " + obj2);
                work();
            }
            System.out.println(name + " released lock on " + obj2);
        }
        System.out.println(name + " released lock on " + obj1);
        System.out.println(name + " finished execution.");
    }

    private void work() {
        try {
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在上面的程序中,SyncThread 实现了 Runnable 接口,它通过使用同步块逐个获取锁,从而在两个对象上工作。

在主要方法中,我有三个线程运行 SyncThread,每个线程之间有一个共享资源。线程以这样一种方式运行,即它将能够获得第一个对象的锁定,但是当它尝试获取第二个对象的锁定时,它会进入等待状态,因为它已经被另一个线程锁定。这形成了线程之间资源的循环依赖性,导致死锁。

当我执行上面的程序时,这里是生成的输出,但程序永远不会因 Java 线程中的死锁而终止。

t1 acquiring lock on java.lang.Object@51be9db9
t1 acquired lock on java.lang.Object@51be9db9
t2 acquiring lock on java.lang.Object@7927a829
t2 acquired lock on java.lang.Object@7927a829
t3 acquiring lock on java.lang.Object@3ad2921f
t3 acquired lock on java.lang.Object@3ad2921f

在这里,我们可以清楚地从输出中识别死锁情况,但在实际应用中,很难找到死锁情况并对其进行调试。

How to Detect Deadlock in Java

为了在 Java 中检测死锁,我们需要查看应用程序的Java 线程转储,在最后一篇文章中,我解释了如何使用 VisualVM Profiler 或使用 jstack 实用程序生成线程转储。

这是上述程序的线程转储。

//TODO

How to avoid deadlock in java

这些是我们可以避免大部分死锁情况的一些准则。

  • 避免嵌套锁定:这是死锁的最常见原因,避免锁定另一个资源,如果您已经拥有一个锁定。 如果只使用一个对象锁,则几乎不可能发生死锁情况。例如,下面是没有嵌套锁定的 run() 方法的另一个实现,并且程序成功运行而没有死锁情况。
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name + " acquiring lock on " + obj1);
        synchronized (obj1) {
            System.out.println(name + " acquired lock on " + obj1);
            work();
        }
        System.out.println(name + " released lock on " + obj1);
        System.out.println(name + " acquiring lock on " + obj2);
        synchronized (obj2) {
            System.out.println(name + " acquired lock on " + obj2);
            work();
        }
        System.out.println(name + " released lock on " + obj2);

        System.out.println(name + " finished execution.");
    }
  • 仅锁定需要的:的应该只获取必须处理的资源的锁定,例如,在上述程序中,我锁定了完整的对象资源,但如果我们只对其中一个字段感兴趣,那么我们应该只锁定 具体领域不完整的对象。
  • 避免无限期地等待:如果两个线程正在等待对方使用线程连接无限期完成,则可能发生死锁。 如果您的线程必须等待另一个线程完成,则最好始终使用连接,并等待最长时间等待线程完成。

你可能感兴趣的:(Java)