Java,线程陷入僵局?

时间:2017-04-10 12:27:50

标签: java multithreading deadlock

我的一位朋友向我展示了他的代码,我认为这两个线程可能会陷入僵局,因为他们在尝试获取不同变量的锁时可能会死锁:sb1sb2

当我运行代码时,它们似乎没有死锁,因为我能够看到输出:

A
B
second thread: AB
second thread: BA

以下代码:

public static void main(String[] args) {
        StringBuilder sb1 = new StringBuilder();
        StringBuilder sb2 = new StringBuilder();

        new Thread() {
            public void run() {
                synchronized (sb1) {
                    sb1.append("A");
                    synchronized (sb2) {
                        sb2.append("B");
                        System.out.println(sb1.toString());
                        System.out.println(sb2.toString());
                    }
                }
            }
        }.start();

        new Thread() {
            public void run() {
                synchronized (sb2) {
                    sb2.append("A");
                    synchronized (sb1) {
                        sb1.append("B");
                        System.out.println("second thread: " + sb1.toString());
                        System.out.println("second thread: " + sb2.toString());
                    }
                }
            }
        }.start();
    }

那么这两个线程可能会陷入僵局吗?

4 个答案:

答案 0 :(得分:4)

您发布的代码有潜在的死锁。如果它成功运行,那就意味着你很幸运。

为了演示潜在的死锁,您可以确定时间以确保发生死锁。

public static void main(String[] args) {
    final StringBuilder sb1 = new StringBuilder();
    final StringBuilder sb2 = new StringBuilder();

    new Thread() {
        public void run() {
            synchronized (sb1) {
                sb1.append("A");
                System.out.println("Thread 1 has sync sb1");
                try { Thread.sleep(700); }
                catch (InterruptedException e) { e.printStackTrace(); return; }
                System.out.println("Waiting for thread 1 to sync sb2");
                synchronized (sb2) {
                    sb2.append("B");
                    System.out.println(sb1.toString());
                    System.out.println(sb2.toString());
                }
            }
        }
    }.start();

    new Thread() {
        public void run() {
            try { Thread.sleep(500); }
            catch (InterruptedException e) { e.printStackTrace(); return; }
            synchronized (sb2) {
                System.out.println("Thread 2 has sync sb2");
                sb2.append("A");
                System.out.println("Waiting for thread 2 to sync sb1");
                synchronized (sb1) {
                    sb1.append("B");
                    System.out.println("second thread: " + sb1.toString());
                    System.out.println("second thread: " + sb2.toString());
                }
            }
        }
    }.start();
}

现在第一个线程肯定会在sb1上获得同步,第二个线程将在sb2上获得同步,然后您将陷入僵局。

输出:

Thread 1 has sync sb1
Thread 2 has sync sb2
Waiting for thread 2 to sync sb1
Waiting for thread 1 to sync sb2

答案 1 :(得分:3)

您没有遇到死锁的事实并不意味着不会发生死锁。

当两个线程试图以相反的顺序在两个不同的资源上获取监视器时,你正确地推断出死锁可能

因此,此代码可能会产生死锁。

但是,如果两个线程中的任何一个设法在另一个之前获取两个监视器,则不会发生死锁(这似乎与您的执行一起发生)。

以下是如何发生死锁:

  1. 线程1启动并获取sb1
  2. 上的锁定
  3. 线程2启动并获取sb2
  4. 上的锁定
  5. 线程1等待获取sb2上的锁定,该锁定由第二个线程
  6. 拥有
  7. 线程2等待获取sb1上的锁定,该锁定由第一个线程
  8. 拥有

    由于没有线程会释放它的锁并且两个都在等待,所以你会遇到死锁。

    注意:正如Khelwood建议的那样,强制线程进入休眠状态可能会阻止其中一个线程首先获取这两个锁,从而产生死锁。

答案 2 :(得分:1)

该代码解释了一个简单的死锁。

最容易辨别的方法基本上是因为你的Threads彼此之间存在循环依赖关系。

此代码很多次都会导致死锁。

答案 3 :(得分:0)

是的,您的程序可能会陷入僵局。这是一个基于@khelwood答案的更动态的例子。它为实际的字符串附加添加了一些延迟,并在while循环中重复它。所以你可以看到正在发生的事情,迟早会导致程序陷入僵局。要识别死锁情况,请使用ThreadMXBean.findDeadlockedThreads()。

package stack43323164;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.List;

import java.lang.management.ThreadInfo;

public class HowToDemonstrateDeadlock {

    private static List<ThreadInfo> findDeadlocks() {
        ThreadMXBean tmxb = ManagementFactory.getThreadMXBean();
        long[] result = tmxb.findDeadlockedThreads();
        if (result == null)
            return java.util.Collections.emptyList();
        return java.util.Arrays.asList(tmxb.getThreadInfo(result, 2));
    }

    public static void main(String[] args) {

        final StringBuilder sb1 = new StringBuilder();
        final StringBuilder sb2 = new StringBuilder();

        long monitorDelay=1000L;
        //You can play with the delay times to modify the results
        long threadOneDelay=100L;
        long threadTwoDelay=100L;

        new Thread() {
            public void run() {
                try {
                    while (true) {
                        synchronized (sb1) {
                            sb1.append("A");
                            System.out.println("Thread 1 has sync sb1");
                            System.out.println("Waiting for thread 1 to sync sb2");
                            synchronized (sb2) {
                                sb2.append("B");
                                System.out.println(sb1.toString());
                                System.out.println(sb2.toString());
                            }
                        }
                        Thread.sleep(threadOneDelay);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();

        new Thread() {
            public void run() {
                try {
                    while (true) {
                        synchronized (sb2) {
                            System.out.println("Thread 2 has sync sb2");
                            sb2.append("A");
                            System.out.println("Waiting for thread 2 to sync sb1");
                            synchronized (sb1) {
                                sb1.append("B");
                                System.out.println("second thread: " + sb1.toString());
                                System.out.println("second thread: " + sb2.toString());
                            }
                        }
                        Thread.sleep(threadTwoDelay);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();

        new Thread() {
            public void run() {
                try {
                    while (true) {
                        List<ThreadInfo> deadlocks = findDeadlocks();
                        if (!deadlocks.isEmpty()) {
                            for (ThreadInfo i : deadlocks) {
                                System.out.println("Deadlock detected on thread " + i.getThreadId() + "\n" + i);
                            }
                            //Not a chance to solve the situation - boom
                            System.exit(1);
                        } else {
                            System.out.println("No deadlock so far.");
                        }
                        Thread.sleep(monitorDelay);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}