2013-04-01 4 views
1

Ниже приведен пример, который я нашел о взаимоблокировках, и он работает. Мой вопрос: как работает currentThread()? А также A и B создаются не как потоки, а конкретно:Понимание взаимоблокировок в java

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(); 
    } 
} 
+1

Что вы имеете в виду, как это currentThread работа? Он возвращает текущий поток. Создается поток, первый поток все еще существует. –

+1

'' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' 'и ' 'currentThread' возвращает поток, из которого был вызван метод; как это работает? Взгляните на исходный код. –

+0

Где создается нить A? Я новичок в java. @Dave Newton – srk

ответ

1

Некоторые хорошие ответы здесь (+1 к @Ralf и @ bmorris591), но я думал, что объяснить свой код немного больше.

Здесь 2 темы. «Основной» поток, который запускает main(...) и "RacingThread", начинается с конструктора Deadlock. Btw, очень плохая форма, чтобы начать поток в конструкторе объекта. Было бы лучше, чтобы сделать что-то вроде:

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

Кроме того, внутри Deadlock конструктора он называет Thread.currentThread().setName("MainThread");. Это пытается установить имя текущего текущего потока, который (путано) является «основным» потоком, так как он сделал new Deadlock(). К сожалению, вызов setName(...) является noop, если поток уже запущен, поэтому он ничего не делает.

Далее, внутри Deadlock конструктора, то "RacingThread" построен с this как Runnable и start() называется, которая разветвляется нить и она вызывается метод Deadlock.run(). Это занимает некоторое время, поэтому, скорее всего, линия a.foo(b); достигнута до вызывается метод run().

Ваши a и b объектов: не темы, как уже указывалось. Это просто объекты, которые используются для демонстрации блокировки. Тупик происходит потому, что основной поток вызывает a.foo(b);, а затем "RacingThread" вызывает b.bar(a); внутри метода run();. a.foo(...) является synchronized по телефону a, а затем пытается позвонить b.last(), который является synchronized на b. b.bar(...) является synchronized по телефону b, а затем пытается позвонить a.last(), который является synchronized по телефону a. Это классический тупик.

Надеюсь, это поможет.

0

Я думаю, вы считаете, что синхронизированное ключевое слово является глобальной блокировкой. Это не. Он просто блокирует (сериализует) доступ к методу, к которому он применяется. Учитывая, что ваш пример не вызывает тот же метод в одном классе из двух отдельных потоков, нет причин, по которым он не будет работать.

+0

Мои 2 цента также, я вижу, где это не должно мешать операциям в этом случае. – kabuto178

+0

Ключевое слово 'synchronized' на методе блокирует _объект_, а не метод. 'b.bar()' и 'b.last()' блокировать тот же _объект_. – Gray

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.

Эти стопки пытаются приобрести блокировки на двух объектах Lock в обратном порядке.

So Locker1 замки LOCKA затем ждет несколько миллисекунд. Locker2 замки LOCKB и ждет несколько миллисекунд, они пытаются и приобретают другой замок.

Ситуация такова, что Locker1 ждет LOCKB и Locker2 ждет LOCKA навсегда, как другой поток никогда не освобождает его.

Вы можете увидеть это довольно ясно в отвал нити для этих потоков:

"pool-1-thread-1" - Thread [email protected] 
    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" [email protected] 
    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 [email protected] 
    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" [email protected] 
    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 методы в двух Object с, а не два Lock объектов.

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

Вы поймете лучше сейчас?

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 
Смежные вопросы