看看下面这个程序:class ThreadA
{
    public static void main(String[] args)
    {
      ThreadB b=new ThreadB();
      b.start();
      System.out.println("b is start....");
      synchronized(b)
      {
        try
        {
            System.out.println("Waiting for b to complete...");
             b.wait();
          System.out.println("Completed.Now back to main thread");
        }catch (InterruptedException e){}
      }
      System.out.println("Total is :"+b.total);
     }
}
class ThreadB extends Thread
{
    int total;
    public void run()
    {
     try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
    synchronized(this)
    {
        System.out.println("ThreadB is running..");
        for (int i=0;i<100;i++ )
        {
          total +=i;
          System.out.println("total is "+total);
        }
        //this.notify();
    }
    }
}运行结果:
b is start....
Waiting for b to complete...
ThreadB is running..
total is 0
total is 1
.....
total is 4851
total is 4950
Completed.Now back to main thread
Total is :4950疑问:由于B线程会sleep(2000).因此A线程会首先获得公共对象b的锁进入运行。然后打印System.out.println("Waiting for b to complete...");接着等待阻塞: b.wait();。此时线程A放弃b的锁。而线程B则进入循环运行,开始打印
ThreadB is running..
total is 0
total is 1
.....
total is 4851
total is 4950
但是很奇怪B线程运行完毕以后,并没有调用//this.notify();这个时候A线程是不是不会被激活?那么程序为什么可以正常结束而且打印了A线程中wait()之后的两条打印语句呢?

解决方案 »

  1.   

    notify只是唤醒了一个因为调用了wait而自愿阻塞的线程,并不是释放了线程锁,释放线程锁 是因为ThreadB 已经执行完了,所以线程锁自动释放,ThreadA得到锁 继续执行
      

  2.   

    接上,比如说我在ThreadB没有执行完之前 notify()。同样 a因为线程b没有执行完 还是会阻塞的public class ThreadA {
    public static void main(String[] args) {
    ThreadB b = new ThreadB();
    b.start();
    System.out.println("b is start....");
    synchronized (b) {
    try {
    System.out.println("Waiting for b to complete...");
    b.wait();
    System.out.println("Completed.Now back to main thread");
    } catch (InterruptedException e) {
    }
    }
    System.out.println("Total is :" + b.total);
    }
    }class ThreadB extends Thread {
    int total; public void run() {
    try {
    Thread.sleep(2000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    synchronized (this) {
    System.out.println("ThreadB is running..");
    this.notify();//在这里唤醒等待的线程,没用,因为这个现在还没有执行完
    for (int i = 0; i < 100; i++) {
    total += i;
    System.out.println("total is " + total);
    } }
    }
    }
      

  3.   

    还有有一点没有搞清楚: 当A线程执行b.wait()之后,A线程会被阻塞,这个阻塞当什么条件的时候才会激活??我原来一直认为b.wait()阻塞以后,只有b.notifyAll()才能将被阻塞的线程激活。这个理解是不是正确的呢?
      

  4.   

    当A线程执行b.wait()之后,A线程会被阻塞,A要想运行 ,除非B运行同步方法已经结束,或者B也自愿wait(),A才能得到B的锁继续执行
      

  5.   

    如果你改成b.wait();这b也自愿阻塞,但因为你在A中加了b的同步方法,虽然b也阻塞了,但A还在等待B执行结束。。及变成了死锁的状态,a等待b,b等待apackage com.lee.amax.baoshu;public class ThreadA {
    public static void main(String[] args) {
    ThreadB b = new ThreadB();
    b.start();
    System.out.println("b is start....");
    synchronized (b) {
    try {
    System.out.println("Waiting for b to complete...");
    b.wait();
    System.out.println("Completed.Now back to main thread");
    } catch (InterruptedException e) {
    }
    }
    System.out.println("Total is :" + b.total);
    }
    }class ThreadB extends Thread {
    int total; public void run() {
    try {
    Thread.sleep(2000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    synchronized (this) {
    System.out.println("ThreadB is running..");
    // this.sleep(4000);
    try {
    this.wait();//改成这样既出现死锁状态,最好改成wati(time);
    } catch (InterruptedException e) {
    // TODO 自动生成 catch 块
    e.printStackTrace();
    }

    for (int i = 0; i < 100; i++) {
    total += i;
    System.out.println("total is " + total);
    } }
    }
    }
      

  6.   

    建议你看下下面的帖子
    http://topic.csdn.net/u/20091010/22/f3b47259-14c8-4250-b52b-8a57584a683c.html
      

  7.   


    这个帖子的代码我自己看过了,我修改如下:
    class T2 extends Thread {
        public void run() {
            for (char i= 'A'; i< 'F'; ++i) {
                synchronized(Main.lockObj) {
                    System.out.println(i);
                  //  Main.lockObj.notify(); 注意我讲notify注释掉
                }
                try {
                    sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }            
            }
        }
    }
    如果我将T2中的notify注释掉,很明显整个代码的运行将出现死锁,这主要是因为T1中的Main.lockObj.wait();把t1线程阻塞之后没有Main.lockObj.notify();条语句将t1激活照成的t1永远无法运行的死锁。但LZ的疑问是:LZ的代码中ThreadB的 //this.notify();也被注释掉了,但运行的时候ThreadA并没有出现死锁,我还是不知道为什么,谢谢musiclee的热情解释!
      

  8.   

    汗你还是没有明白啊 。。A线程先运行,然后调用b.wait();则A就得等B先运行完,无论如何B运行完了A才可以运行,
    那么在B调用this.notify();  以下是api
    唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。 
    直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。 
    你看到一句没  "直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程"  虽然B线程主动的this.notify();但是B线程仍然没有执行完毕,他还占有线程锁。。所以A还是无法运行,直到它执行完,释放了锁以后,A才进行运行。
    至于为什么B运行完毕后不显示的this.notify();A也可以运行,我估计A只要侦测到实例b的线程锁完毕 ,则线程A就会自动运行
     
      

  9.   

    我觉得你没看懂我的意思:我理解:“直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程”。我很清楚只要ThreadB 运行完this.notify()之后就让等待线程ThreadA被唤醒,然后ThreadB执行完自己在run中synchronized快中的全部代码后会放弃对象b的锁,然后ThreadA开始获得b的锁继续执行下去。但我的问题是:我现在吧ThreadB中的this.notify()语句注释掉了,也就是不去唤醒等待线程ThreadA,奇怪的是LZ的代码仍然可以正常结束,也就是说没有notify,ThreadA在wait()之后仍然被唤醒了,这一点我很不理解。我一直都认为被wait的线程,一定要对象监视器notify去唤醒,否则无法醒来,就更不存在去获取锁了。
      

  10.   

    我明白你的意思,this.notify()把它注释起来,我也试了
    因为this.notify()的意思并不是释放掉线程锁。this.notify()不等于释放线程锁,。。它是唤醒,不是释放! 及告诉A你可以来试试看看门上锁没,并不是把门的钥匙给了A结不结贴不重要,重要的是 把问题弄明白,(不然提问干什么 )呵呵 : )
      

  11.   

    A会一直去试着开门,知道A可以打开门 A才进去。。(B线程的执行完毕就是得到钥匙)
      

  12.   

    哎又仔细的看了下api现在我也被搞晕了。
      

  13.   

    呵呵,你的理解我是认同的,notify确实唤醒在公共数据对象中被阻塞的线程,这些线程醒来以后就会尝试去获取对象的锁,如果获取不到,就仍然会等待锁。这是由于synchronized块中的代码已经被原子操作了。我原来也一直是怎么考虑问题的,但是LZ得代码让我迷惑了,毕竟this.notify被注释之后,就不会唤醒被等待的线程,那么ThreadA既然不会被唤醒,自然也就运行不了,可运行结果我却无法理解,ThreadA在ThreadB没有b.notify且自动运行结束后的情况下,既然奇迹般的自己醒来了,而且继续运行了下去。我觉得,问题是在公共数据区b上,因为公共数据区b是线程ThreadB类型的。是不是ThreadB运行完了以后,自动退出了虚拟机,那么在b对象上的所有等待线程ThreadA也就不存在继续等待了呢???还是挺纳闷的????