了解java中的死锁

时间:2013-04-01 14:29:32

标签: java multithreading deadlock

以下是我发现的关于死锁的一个例子,它有效。我的问题是currentThread()如何运作?并且A和B也不是专门创建的线程,即:

Deadlock d=new Deadlock();
Thread A=new Thread(d) 

代码如何运作?

class A {
    synchronized void foo(B b) {
        String name = Thread.currentThread().getName();
        System.out.println(name + " entered A.foo");
        try {
            Thread.sleep(1000);
        } catch (Exception e) {System.out.println("A Interrupted");
        }
        System.out.println(name + " trying to call B.last()");
        b.last();
    }

    synchronized void last() {
        System.out.println("Inside A.last");
    }
}

class B {
    synchronized void bar(A a) {
        String name = Thread.currentThread().getName();
        System.out.println(name + " entered B.bar");
        try {
            Thread.sleep(1000);
        } catch (Exception e) {System.out.println("B Interrupted");
        }
        System.out.println(name + " trying to call A.last()");
        a.last();
    }
    synchronized void last() {
        System.out.println("Inside A.last");
    }
}

public class Deadlock implements Runnable {
    A a = new A();
    B b = new B();

    Deadlock() {
        Thread.currentThread().setName("MainThread");
        Thread t = new Thread(this, "RacingThread");
        t.start();
        a.foo(b); // get lock on a in this thread.
        System.out.println("Back in main thread");
    }

    public void run() {
        b.bar(a); // get lock on b in other thread.
        System.out.println("Back in other thread");
    }

    public static void main(String args[]) {
        new Deadlock();
    }
}

5 个答案:

答案 0 :(得分:2)

我认为这是一个相当混乱的死锁示例 - 它会给问题增加其他噪音。

使用Lock这样的对象可以实现一个非常简单的例子:

public class App {

    private static final Lock LOCKA = new ReentrantLock();
    private static final Lock LOCKB = new ReentrantLock();

    private static final class Locker1 implements Runnable {

        @Override
        public void run() {
            while (true) {
                try {
                    LOCKA.lockInterruptibly();
                    Thread.sleep(100);
                    LOCKB.lockInterruptibly();
                    System.out.println("Locker 1 Got locks");
                } catch (InterruptedException ex) {
                    return;
                }
                LOCKB.unlock();
                LOCKA.unlock();
            }
        }
    }

    private static final class Locker2 implements Runnable {

        @Override
        public void run() {
            while (true) {
                try {
                    LOCKB.lockInterruptibly();
                    Thread.sleep(100);
                    LOCKA.lockInterruptibly();
                    System.out.println("Locker 2 Got locks");
                } catch (InterruptedException ex) {
                    return;
                } finally {
                    LOCKA.unlock();
                    LOCKB.unlock();
                }
            }
        }
    }

    public static void main(String[] args) throws IOException {
        final ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.submit(new Locker1());
        executorService.submit(new Locker2());

    }
}

应用程序在执行程序中启动两个线程,然后我们让这些线程调用两个runnables。

这些runnables尝试以相反的顺序获取两个Lock对象的锁。

所以Locker1锁定LOCKA然后等待几毫秒。 Locker2锁定LOCKB并等待几毫秒,他们尝试获取另一个锁。

情况是Locker1等待LOCKBLocker2等待LOCKA,因为另一个线程永远不会释放它。

您可以在这些线程的线程转储中相当清楚地看到这一点:

"pool-1-thread-1" - Thread t@8
   java.lang.Thread.State: WAITING
    at sun.misc.Unsafe.park(Native Method)
    - waiting to lock <7725204d> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) owned by "pool-1-thread-2" t@9
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:894)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221)
    at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
    at com.boris.testbench.App$Locker1.run(App.java:32)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
    at java.util.concurrent.FutureTask.run(FutureTask.java:166)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:722)

   Locked ownable synchronizers:
    - locked <7567e1fa> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)

    - locked <5ad52411> (a java.util.concurrent.ThreadPoolExecutor$Worker)

"pool-1-thread-2" - Thread t@9
   java.lang.Thread.State: WAITING
    at sun.misc.Unsafe.park(Native Method)
    - waiting to lock <7567e1fa> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) owned by "pool-1-thread-1" t@8
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:894)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221)
    at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
    at com.boris.testbench.App$Locker2.run(App.java:51)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
    at java.util.concurrent.FutureTask.run(FutureTask.java:166)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:722)

   Locked ownable synchronizers:
    - locked <7725204d> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)

    - locked <6856c528> (a java.util.concurrent.ThreadPoolExecutor$Worker)

我们可以看到pool-1-thread-1希望锁定pool-1-thread-2拥有的锁定,而pool-1-thread-2希望锁定pool-1-thread-1拥有的锁定。

这种情况将永远持续下去,因此陷入僵局。

您的代码实现了相同的结果,但不是使用手动生成的两个线程,而是使用应用程序主线程(由JVM生成)和一个手动生成的线程。

它还在两个synchronized s而不是两个Object对象中使用Lock个方法。

答案 1 :(得分:2)

Thread#currentThread() =当前正在运行的线程。一切都在线程中运行。当你启动一个java应用程序时,你只有一个我们可以调用主线程的线程。因此,调用main方法只不过是正在运行的线程的开始。

我评论你可能有些疑惑。

// Deadlock is a Runnable. So, it can be wrapped inside a Thread Object to be started.
public class Deadlock implements Runnable {
    A a = new A();
    B b = new B();

    Deadlock() {
        // currentThread now is the one that instanciated this Deadlock object.
        Thread.currentThread().setName("MainThread");
        // here the Deadlock is wrapped inside a thread object. Notice the `this` qualifier.
        Thread t = new Thread(this, "RacingThread");
        // here the thread wrapping deadlock is started.
        t.start();

        a.foo(b); // get lock on a in this thread.
        System.out.println("Back in main thread");
    }

    public void run() {
        b.bar(a); // get lock on b in other thread.
        System.out.println("Back in other thread");
    }

    // here is the start of the Main Thread! :D 
    public static void main(String args[]) {
        // the program is started inside the Deadlock Class constructor.
        new Deadlock();
    }
}

你现在能更好地理解吗?

答案 2 :(得分:1)

这里有一些好的答案(+1给@Ralf和@ bmorris591),但我想我会更多地解释你的代码。

这里有2个主题。运行main(...)"RacingThread"的“主”线程从Deadlock构造函数中开始。顺便说一句,在对象构造函数中启动一个线程是非常糟糕的形式。做一些像这样的事情会更好:

  Deadlock deadlock = new Deadlock();
  new Thread(deadlock, "RacingThread").start();

此外,在Deadlock构造函数中,它调用Thread.currentThread().setName("MainThread");。这是尝试设置当前正在运行的线程的名称,因为它执行了new Deadlock(),因此(令人困惑地)是“主”线程。不幸的是,如果线程已经在运行,那么setName(...)调用是noop,因此它不会执行任何操作。

接下来,在Deadlock构造函数中,"RacingThread"this构建,Runnablestart()被调用,它会分叉线程并拥有它调用Deadlock.run()方法。这需要一些时间,因此在调用a.foo(b);方法之前,很可能会在之前到达run()行。

您的ab个对象不是线程已经指出了。它们只是用于演示锁定的对象。发生死锁是因为主线程正在调用a.foo(b);,然后"RacingThread"调用b.bar(a);方法内的run();a.foo(...)上的synchronizeda,然后尝试在b.last()上致电synchronized bb.bar(...)上的synchronizedb,然后尝试在a.last()上致电synchronized a。这是一个典型的僵局。

希望这有点帮助。

答案 3 :(得分:0)

我认为您假设synchronized关键字是全局锁定。它不是。它只是锁定(序列化)访问它所应用的方法。鉴于您的示例不会从2个单独的线程在同一个类上调用相同的方法,因此没有理由不起作用。

答案 4 :(得分:0)

当2个线程以不同的顺序获取两个资源时发生死锁:

  • T1取r1
  • T2 take r2
  • T1尝试阻止r2&lt; ===阻塞,等待资源r2
  • T2尝试阻止r1&lt; ===阻塞,等待资源r1
  • system dead

这个程序:

public class DeadLock implements Runnable {

   String name;
   Object r1;
   Object r2;

   DeadLock( String name, Object r1, Object r2 ) {
      this.name = name;
      this.r1   = r1;
      this.r2   = r2;
   }

   @Override public void run() {
      if( name.equals( "T1" )) {
         System.out.println( name + " try to take r1" );
         synchronized( r1 ) {
            System.out.println( name + " has taken r1" );
            try{ Thread.sleep( 1000L ); }catch( InterruptedException x ){}
            System.out.println( name + " try to take r2" );
            synchronized( r2 ) {
               System.out.println( name + " has taken r2" );
            }
         }
      }
      else {
         System.out.println( name + " try to take r2" );
         synchronized( r2 ) {
            System.out.println( name + " has taken r2" );
            try{ Thread.sleep( 1000L ); }catch( InterruptedException x ){}
            System.out.println( name + " try to take r1" );
            synchronized( r1 ) {
               System.out.println( name + " has taken r1" );
            }
         }
      }
   }

   public static void main( String[] args ) {
      Object r1 = new Object();
      Object r2 = new Object();
      new Thread( new DeadLock( "T1", r1, r2 )).start();
      new Thread( new DeadLock( "T2", r1, r2 )).start();
   }
}

输出:

T1 try to take r1
T2 try to take r2
T1 has taken r1
T2 has taken r2
T2 try to take r1
T1 try to take r2