`
standalone
  • 浏览: 595982 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

再谈Java的wait(), sleep(), notify()和notifyAll()

    博客分类:
  • java
阅读更多
一段时间不用java,这些概念就全混淆了,有必要彻底澄清一下,总结在这里,当然以我的本事,不太可能写出水平较高的总结,这里主要是总结stackoverflow上面高人的言论。

先说sleep() 和 wait()

sleep() method causes the current thread to move from running state to block state for a specified time. If the current thread has the lock of any object then it keeps holding it, which means that other threads cannot execute any synchronized method in that class object.

sleep()这个方法是让当年线程从运行态转入阻塞态一段时间,如果当前线程拥有一些对象的锁,那么这个线程将继续拥有,也就意味着其他线程不能执行那些对象的synchronized方法。

注意,sleep()是Thread类的static方法,假如在一个Synchronized块中调用Thread的sleep()方法,虽然线程休眠,但是对象锁还会存在,

wait() method causes the current thread to go into block state either for a specified time or until notify, but in this case the thread releases the lock of the object (which means that other threads can execute any synchronized methods of the calling object.

wait()方法使当前的线程进入阻塞状态直到一段指定的时间结束或者被唤醒,调用wait()会释放对象的锁,也就意味着其他线程可以执行对象的synchronized方法。

注意,wait()方法是Object类的非静态方法,所以每个对象都可以调用。

wait()的使用

a). wait()必须放在synchronized块之中;
这个我前面有一篇blog刚好说这个的:
http://standalone.iteye.com/blog/788259
要执行wait()当前线程必须要先拿到锁。
b). wait()必须放在一个while loop中;
You need not only to loop it but check your condition in the loop. Java does not guarantee that your thread will be woken up only by a notify()/notifyAll() call or the right notify()/notifyAll() call at all. Because of this property the loop-less version might work on your development environment and fail on the production environment unexpectedly.
这个是说有可能睡眠的线程被唤醒不是靠的被notify()/notifyAll()或者被中断,或者时间到了,有一种情况叫spurious wakeup, 你可以点进去看一下高人对此种虚假唤醒的解释。总之,因此要将wait放在一个while循环中,每次醒来自己去检查你的条件是否满足了。

Orcacle的文档也讲了:

A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops, like this one:

     synchronized (obj) {
         while (<condition does not hold>)
             obj.wait(timeout);
         ... // Perform action appropriate to condition
     }


http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#wait(long)

还有另一种情况,看下面这个例子,如果不用while循环会出现什么错误。
(引自:http://stackoverflow.com/questions/37026/java-notify-vs-notifyall-all-over-again

public synchronized void put(Object o) {
    while (buf.size()==MAX_SIZE) {
        wait(); // called if the buffer is full (try/catch removed for brevity)
    }
    buf.add(o);
    notify(); // called in case there are any getters or putters waiting
}

public synchronized Object get() {
    // Y: this is where C2 tries to acquire the lock (i.e. at the beginning of the method)
    while (buf.size()==0) {
        wait(); // called if the buffer is empty (try/catch removed for brevity)
        // X: this is where C1 tries to re-acquire the lock (see below)
    }
    Object o = buf.remove(0);
    notify(); // called if there are any getters or putters waiting
    return o;
}


假设这是一个ProducerConsumer类的两个方法,假如Consumer c1进入同步方法并且buffer是空的,c1会被阻塞在X处。这时候假如c2等着要进同步方法,它必须要拿到同步锁才能进来,所以会被阻塞在get()方法的开头处Y;假设Producer p1这时候生成了一个东西添加到buffer里面去了,然后notify(),此时因为wait的只有c1,所以c1会被唤醒,等着去拿同步锁,好继续往下执行。现在的情形是c1和c2都在抢同步锁!再假如c2幸运拿到了,那么c2会完整执行get()方法并且释放同步锁,这时候c1可以拿到,注意假如我们没有while循环,c1此时拿到锁之后直接往下执行,等着它的是一个空的buffer!会报IndexArrayOutOfBoundException!解决的办法就是需要while循环!


notify() vs. notifyAll()

notify() 是唤醒一个线程,notifyAll()是唤醒所有的线程,这个大家都知道。多数时候你应该用notifyAll,有时候如果需要用notifyAll()错用notify()会出错!仍然看上面那个例子。

简化情况,假如buffer的大小是1.
假如有如下的调用顺序:(P代表生产者线程,C代表消费者线程)

1:
P1放1个东西到buffer;

2:
P2准备放--发现已经满了--wait

3:
P3准备放--发现已经满了--wait

4:
C1准备从buffer里取一个,并成功
C2准备从buffer里取一个,阻塞在方法入口
C3准备从buffer里取一个,阻塞在方法入口

5:
C1释放同步锁,调用notify(),退出get方法
P2被唤醒
但是C2在P2之前抢到锁,所以P2会被继续阻塞
C2检查wait条件,发现buffer仍为空,继续阻塞

6:
现在P3,C2,C3都在等待
最终P2拿到锁,放一个东西到buffer, 调用notify,退出

7:
P2唤醒了P3(记住是随机的)
P3检查了一下发现buffer是满的,等待
现在没有任何线程再会调用notify,所有线程会被永远阻塞!死锁产生!

解决方案:将notify替换成notifyAll




分享到:
评论

相关推荐

    sleep()、wait()、yield()和join()方法特点及区别.docx

    3.wait,notify和notifyAll只能在同步方法或同步代码块中调用,而sleep可以在任何地方调用; 4.sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常。(如果不是在同步方法或同步代码块中调用wait()方法,...

    java 多线程设计模式 进程详解

    wait()、notify()和notifyAll() wait()和sleep() 线程中断 静态方法(有关同步的细节) 总结 第五章 Java线程编程的例子 数据结构和容器 简单的同步例子 一个网络服务器类 AsyncInputStream类 使用TCP...

    100家大公司java笔试题汇总

    wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。) sleep...

    java线程学习笔记

    一 基本知识 2 1.1 任务Runnable 2 1.2 线程构造器Threat 2 1.3 执行器Executor 2 1.4 任务中返回值Callable接口 3 1.5 休眠 6 1.6 让步 6 1.7 优先级 6 1.8 后台线程(daemon) 6 ...2.5.3 notify()与notifyAll() 24

    java笔试题大集合及答案(另附各大公司笔试题)

    wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。 62、同步和...

    高级开发并发面试题和答案.pdf

    描述一下notify和notifyAll区别; synchronized关键字加在静态方法和实例方法的区别; 用锁的注意点; cas机制可能导致的问题ABA,什么是ABA; 程序开多少线程合适; 实现一下DCL(双重检查锁) stream 和 parallel...

    Java常见面试题208道.docx

    42.notify()和 notifyAll()有什么区别? 43.线程的 run()和 start()有什么区别? 44.创建线程池有哪几种方式? 45.线程池都有哪些状态? 46.线程池中 submit()和 execute()方法有什么区别? 47.在 java 程序中怎么...

    LeetCode判断字符串是否循环-java:Java

    LeetCode判断字符串是否循环 知识点总结 java基础 1、使用迭代器和for each循环查看集合元素时只能获得元素的值,不能改变元素 ...object的方法:wait(notify,notifyall),thread的方法:(sleep、join),结束

    史上最全java面试,103项重点知识,带目录

    42. notify()和 notifyAll()有什么区别? 16 43. 线程的 run()和 start()有什么区别? 16 44. 创建线程池有哪几种方式? 17 45. 线程池都有哪些状态? 18 46. 线程池中 submit()和 execute()方法有什么区别? 18 49....

    java 面试题 总结

    wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。 14、Overload...

    这就是标题—— JUC.pdf

    wait()、notify()和notifyAll() 虚假唤醒 Condition 定制化通信 多线程锁 并发下的集合类 List Set Map Callable接口 线程创建的方式 callable / runnable FutureTask JUC常用辅助类 CountDownLatch (减少计数器) ...

    生产者消费者JAVA课程设计

    //下面的消费者类对象和生产者类对象所操作的是同一个同步堆栈对象 Producer p1 = new Producer(stack,ce); new Thread(p1).start();//生产者线程启动 Consumer c1 = new Consumer(stack,ce); new Thread(c1)....

    超级有影响力霸气的Java面试题大全文档

    wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。 17、...

    关于JAVA面试的100题及其答案

    wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。

    Java测试题2答案

    DEF A notify() B wait() C notifyAll() D sleep() E.yield() F.synchronized(this) 7.构造BufferedInputStream的合适参数是哪个? AC A BufferedInputStream B BufferedOutputStream ...

    java面试题,180多页,绝对良心制作,欢迎点评,涵盖各种知识点,排版优美,阅读舒心

    notify notifyAll 78 【多线程】sleep()和wait()分别是哪个类的方法,有什么区别? 79 【多线程】sleep()和yield()的区别 79 【多线程】对synchronized理解?用在代码块和方法上有什么区别? 80 【多线程】Volatile...

    JAVA程序设计教程

    第一章程序和程序设计 .......................................................................................................1 §1.1 什么是程序 ?........................................................

    jstack生成的Thread Dump日志.docx

    只有当别的线程在该对象上调用了 notify()或者notifyAll()方法,"Wait Set"队列中的线程才得到机会去竞争,但是只有一个线程获得对象的Monitor,恢复到运行态。"Wait Set"中的线程在Thread Dump中显示的状态为 in ...

    进销存系统文档作业例子

    wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。 14、...

    汪文君高并发编程实战视频资源全集

    │ 高并发编程第一阶段27讲、wait和sleep的本质区别是什么,深入分析(面试常见问题).mp4 │ 高并发编程第一阶段28讲、线程生产者消费者的综合实战结合Java8语法.mp4 │ 高并发编程第一阶段29讲、如何实现一个...

Global site tag (gtag.js) - Google Analytics