3

以下代码在 SCJP6 书中

class ThreadA {
    public static void main(String [] args) {
        ThreadB b = new ThreadB();
        b.start();

        synchronized(b) {
            try {
                System.out.println("Waiting for b to complete...");
                b.wait();
            } catch (InterruptedException e) {}
            System.out.println("Total is: " + b.total);
        }
    }
}

class ThreadB extends Thread { 
     int total;

     public void run() {
         synchronized(this) {
             for(int i=0;i<100;i++) {
                 total += i;
             }
             notify();
         }
     }
 }

前面的代码不会导致死锁,因为线程 a 和 b 都在 b 上锁定(在各自的同步块中)?

我错过了一些东西,但不太确定它是什么。

4

2 回答 2

6

最可能的执行如下:

  • b.start()run正在执行的方法之间有一点延迟
  • 因此,主线程设法获得锁b并进入synchronized
  • 然后它等待b释放锁
  • run开始执行时,监视器可用(或将很快可用),因此它可以进入synchronized
  • 完成后通知b它可以停止等待
  • 主线程完成。

run但是,根据线程调度,先执行也不是不可能,在这种情况下,主线程可能会永远等待b.wait()。例如,如果您通过Thread.sleep(100)在 之后插入一个小的来帮助这种情况b.start(),您应该观察这种行为。

Bottom line: it is a smelly code that could encounter liveness issues (it is not a deadlock per se since the lock is available).

于 2013-03-29T11:05:16.540 回答
4

这取决于。

等待方法的文档中 -

使当前线程等待,直到另一个线程为此对象调用 notify() 方法或 notifyAll() 方法。换句话说,此方法的行为与它只是执行调用 wait(0) 完全相同。

当前线程必须拥有该对象的监视器。线程释放此监视器的所有权并等待,直到另一个线程通过调用 notify 方法或 notifyAll 方法通知在此对象的监视器上等待的线程唤醒。然后线程等待直到它可以重新获得监视器的所有权并恢复执行。

所以,如果你考虑这两种情况 -

  • 如果先ThreadA获得对象的锁b,它会等待,导致锁的释放,这将导致ThreadB它继续工作。
  • 如果ThreadB先获得锁,那么它会继续工作,释放锁,然后ThreadA开始。接下来,ThreadA将等待对象 lock b,这可能会导致它永远等待。
于 2013-03-29T11:05:09.410 回答