挺奇怪的,不能确定是什么原因。
不过我在哪本书上看到过,即使没有别的线程来notify,正在wait的线程也是有可能被唤醒的。

解决方案 »

  1.   

    to miwoo:
       还有这么神奇的,那java中的信号量机制还能用吗?
      

  2.   

    >那java中的信号量机制还能用吗<
    不明白你的意思。
      

  3.   

    我靠信号量控制我的线程执行,现在即使没有别的线程来notify,正在wait的线程也是有可能被唤醒的话,我又怎么控制了?
      

  4.   

    synchronized(obj){
        try{
          obj.wait();
        }catch (Exception e){} 
      }
    wait()是不能这么写的,要这样synchronized(obj){
      while(没有资源){
        obj.wait();
      }
    }
    看看effective java吧。
      

  5.   

    to miwoo:
      拜托请说详细一点:
      synchronized(obj){
      while(没有资源){
        obj.wait();
      }
      }
      没有资源指什么?
      

  6.   

    就是wait之前和唤醒之后都必须要判断有没有可用的资源,没有就继续wait
      

  7.   

    楼主发现a和b都被c唤醒了,这个结论一定是你做的有问题,而不是java有问题.
    notify只会唤醒在等待区等待的一个线程.
    俺做了一个单元测试给你看看:
    (1) 第一次sleep之后,result的size为0,代表两个线程都在等待,没有向队列加入元素.
    (2) notify之后,再次sleep,result的size为1而不是2,证明只有一个线程被唤醒.
    <<
    public class TestWaitAndNotify extends TestCase {
        public TestWaitAndNotify(String name) {super(name);}
        private static final List result = new ArrayList();
        private final Object lock = new Object();    private static class ChildThread implements Runnable {
            private final Object lock;
            ChildThread(Object lock) {this.lock=lock;}
            public void run() {
                synchronized(lock) {
                    while(true) {
                        try{lock.wait();}
                        catch(InterruptedException ex) {}
                        synchronized(result) {
                            result.add(this);
                        }
                    }
                }
            }
        }    public void testWaitAndNotify() throws InterruptedException {
            startChildThread();        Thread.sleep(1000);        assertEquals(0, result.size());        synchronized(lock) {
                lock.notify();
            }        Thread.sleep(1000);
            assertEquals(1, result.size());
        }    // private :
        private void startChildThread() {
            Thread child1 = new Thread(new ChildThread(lock));
            child1.setDaemon(true);
            child1.start();        Thread child2 = new Thread(new ChildThread(lock));
            child2.setDaemon(true);
            child2.start();
        }
    }
    >>另外,
    TO miwoo(miwoo)
    <<
    挺奇怪的,不能确定是什么原因。
    不过我在哪本书上看到过,即使没有别的线程来notify,正在wait的线程也是有可能被唤醒的。
    >>
    在"等待区"等待的线程如果要正常地被唤醒,只有notify和notifyAll.当然你可以interrupt等待的线程,但是不用notify/notify你无法唤醒它.
      

  8.   

    >但是不用notify/notify你无法唤醒它<
    你可以看看effective java,一个正在wait的线程即使没有别的线程来notify,也是有可能被唤醒的。
      

  9.   

    TO miwoo(miwoo):
    那一个item? 俺去看看.
      

  10.   

    to xiaohaiz:
      老大,能解释一下为什么要Thread.sleep,是不是因为
    第一个Thread.sleep是等待两个Daemon完全执行,其实不Thread.sleep,assertEquals(0, result.size());也会正确
    第二个Thread.sleep是要等待一线程被唤醒且能得到result锁。
      

  11.   

    to  xiaohaiz(老土进城,两眼通红):
    好像是多线程那部分的第一个item
      

  12.   

    TO itspring(itspring):
    第一个Thread.sleep()是为了保证两个ChildThread已经等待.
    第二个Thread.sleep()是为了保证某个被唤醒的线程已经继续执行.
      

  13.   

    TO miwoo(miwoo):
    呵呵,关于线程的第一个ITEM是:
    <<
    Item 48: Synchronize access to shared mutable data
    >>
    俺又检查了一遍,相信和俺们讨论的问题无关.另外,俺觉得可能是你记错了,再回忆一下JAVA的线程模型.
    JAVA的线程模型主要是同步,靠监视器(monitor)的机制来实现.
    线程和监视器的交互有以下:
    进入监视器.(在入口区:entry set)
    持有监视器.(同时只有一个线程可以持有监视器)
    释放监视器.(线程等待或者同步结束都会释放监视器)
    退出监视器.(同步结束)通过调用wait等待,持有监视器的线程会释放监视器,并且进入"等待区"等待.在等待区中的线程只有通过notify/notifyAll才能被唤醒.被唤醒的线程并没有立刻获取监视器,所有等待区已经被唤醒的线程和入口区的线程会在持有监视器线程释放监视器之后,竞争监视器.这里没有优先级的问题,入口区和等待区的线程机会是一致的(如果线程本身优先级忽略).被唤醒的等待区线程从被唤醒时到获取监视器之间一定存在一个时间差!这个时间差之内,可能会有另外的线程获取监视器并改变了等待线程需要的数据状态,所以要求wait()的使用必须检查数据状态(也就是你上文所说的资源的检查).这也是Effective Java中"Item 50: Never invoke wait outside a loop"的由来.:) Item 50中描述得已经非常清晰了.
      

  14.   

    to xiaohaiz
      不好意思,我没有Effective  Java这本书,在此就顺便问一下。
       Item  50:  Never  invoke  wait  outside  a  loop  中loop就是指你上面的程序              while(true)  {  
                                       try{lock.wait();}  
                                        catch(InterruptedException  ex)  {}  
                                         synchronized(result)  {  
                                            result.add(this);  
                                         }  
                                   } 
    这一段吗?我觉得不要此loop也能得到正确结果。 还有:
       被唤醒的等待区线程从被唤醒时到获取监视器之间一定存在一个时间差!这个时间差之内,可能会有另外的线程获取监视器并改变了等待线程需要的数据状态,
       这里的数据状态应该是指同步时的对象lock吧。象你上面的解释,靠循环也不能解决此问题呀。另外你的解释好像与jdk的文档有出入:
      obj.wait()中有此句:
      Thus, on return from the wait method, the synchronization state of the object and of thread T is exactly as it was when the wait method was invoked。
      此话的意思好像是说,从wait()返回后,同步对象的状态与wait之前的是一样的,这是怎么回事呀,我好糊涂!!
      

  15.   

    不是这个意思. 线程工作有两种方式:
    (1) 互斥.
    (2) 协作.线程是靠同步锁互斥工作的.只有持有同步锁的线程运行,其他的线程都会阻塞直到同步锁的持有者释放.比如下面的代码:
    public class Test {
        private final Object lock = new Object();
        public void someMethod() {
            synchronized(lock) {
            // ........
            }
        }
    }
    无论有多少线程同时调用someMethod方法,synchronized(lock)代码块中的代码只有一个线程能够同时运行.其他的线程都会阻塞.而线程的协作方式是靠wait/notify来进行的.比如线程A的运行需要某种数据状态才能继续,但是线程A本身不能使此数据状态满足.而可能线程B可能满足此数据状态,因此,线程A在数据状态不满足的条件下就需要wait,直到线程B使此状态满足,然后notify线程A继续.
    这样的工作方式就是线程的协作方式,线程A和B通过wait和notify来协同工作.
    典型的例子就是队列,处理线程需要处理队列中的元素,而生产线程需要向队列中加入元素,如果队列中没有任何元素,那么处理线程就必须wait,直到生产线程加入元素后nitofy处理队列.
    来看看代码示例:
    <<
    // 处理线程:
    public class Handler implements Runnable {
        private final List queue;
        Handler(List queue) {this.queue=queue;}
        public void run() {
            synchronized(queue) {
                while(true) {
                    if(queue.isEmpty()) {
                        try{queue.wait();}
                        catch(InterruptedException ex) {}
                    } else {
                        Object element = queue.remove(0);
                        // Handle this element.
                    }
                }
            }
        }
    }
    // 生产线程:
    // 每隔一秒产生一个元素放入到队列后.
    public class Proceduer implements Runnable {
        pirvate final List queue;
        Proceduer(List queue) {this.list=list;}
        public void run() {
            while(true) {
                Object obj = new Object(); // generate element.
                synchronized(queue) {
                    queue.add(obj);
                    queue.notifyAll();
                }            try{Thread.sleep(1000);}
                catch(InterruptedException ex) {}
            }
        }
    }
    // 队列& main
    public class Queue {
        private final static List queue = new LinkedList();
        public static void main(String[] args) {
            new Thread(new Handler(queue)).start();
            new Thread(new Proceduer(queue)).start();
        }
    }>>
    另外,关于你提出的javadoc中的这个解释:
    <<
     obj.wait()中有此句:
    Thus, on return from the wait method, the synchronization state of the object and of thread T is exactly as it was when the wait method was invoked。
    >>
    请看清楚,是对象的同步状态,而不是俺们上文中所描述的"数据状态".
    调用wait方法之前,线程一定要持有同步锁.而wait方法返回之后,代表此线程重新持有了同一个同步锁.所以说wait前后的"同步状态"是一样的.
      

  16.   

    老大:
      你还忘了解释这个问题了。
       Item 50:  Never invoke wait outside a loop 中loop就是指你上面给的第一个程序中 
                    while(true)  {  
                                       try{lock.wait();}  
                                        catch(InterruptedException  ex)  {}  
                                         synchronized(result)  {  
                                            result.add(this);  
                                         }  
                                   } 
    这一段吗?我觉得不要此loop也能得到正确结果。因为数据条件成立后,此线程被唤醒,就可以继续执行了,不需要循环了。