0

下面给出了一个我发现的关于死锁的例子,它有效。我的问题是如何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();
    }
}
4

5 回答 5

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());

    }
}

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

Lock这些可运行对象尝试以相反的顺序获取两个对象上的锁。

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

情况是Locker1等待LOCKB并永远Locker2等待,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两个Objects 而不是两个Lock对象中的方法。

于 2013-04-01T14:58:14.893 回答
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();
    }
}

你现在能明白了吗?

于 2013-04-01T14:59:32.750 回答
1

Some good answers here (+1 to @Ralf and @bmorris591) but I thought I'd explain your code a bit more.

There are 2 threads here. The "main" thread which runs main(...) and the "RacingThread" started from within the Deadlock constructor. Btw, it is very bad form to start a thread in a object constructor. It would be better to do something like:

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

Also, inside the Deadlock constructor it calls Thread.currentThread().setName("MainThread");. This is trying to set the name of the current running thread which is (confusingly) the "main" thread since it did the new Deadlock(). Unfortunately the setName(...) call is a noop if the thread is already running so it doesn't do anything.

Next, inside the Deadlock constructor, the "RacingThread" is constructed with this as the Runnable and start() is called which forks the thread and has it call the Deadlock.run() method. This takes some time so most likely the a.foo(b); line is reached before the run() method is called.

Your a and b objects are not threads as has been pointed out already. They are just objects that are being used to demonstrate the locking. The deadlock happens because the main thread is calling a.foo(b); and then the "RacingThread" calls the b.bar(a); inside of the run(); method. a.foo(...) is synchronized on a and then tries to call b.last() which is synchronized on b. b.bar(...) is synchronized on b and then tries to call a.last() which is synchronized on a. That is a classic deadlock.

Hope this helps a bit.

于 2013-04-01T14:37:34.243 回答
0

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

于 2013-04-01T14:52:27.023 回答
0

当 2 个线程以不同的顺序占用两个资源时,就会发生死锁:

  • T1 取 r1
  • T2 取 r2
  • T1 尝试取 r2 <=== 阻塞,等待资源 r2
  • T2 尝试取 r1 <=== 阻塞,等待资源 r1
  • 系统死机

这个程序:

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
于 2013-04-01T15:00:13.693 回答