`
standalone
  • 浏览: 609715 次
  • 性别: 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




分享到:
评论

相关推荐

    java之wait,notify的用法([ 详解+实例 ])

    Java之wait和notify的用法详解 在Java多线程编程中,wait和notify是两个非常重要的方法,它们都是Object类的方法,用于线程之间的通信和同步。下面我们将详细解释wait和notify的用法。 wait方法 wait方法是Object...

    深入理解Wait、Notify和Wait与sleep区别

    - **权限和锁**:`wait()`, `notify()`和`notifyAll()`需要在同步上下文中使用,而`sleep()`则不需要。 - **资源释放**:`wait()`会释放对象锁,而`sleep()`不会。 - **唤醒机制**:`wait()`需要其他线程调用`notify...

    Java多线程wait和notify

    在Java中,`wait()` 和 `notify()` 方法是实现线程间通信和协作的重要工具,它们属于 `java.lang.Object` 类,这意味着所有类都默认继承了这两个方法。本文将详细探讨如何使用 `wait()` 和 `notify()` 来控制子线程...

    java sleep()和wait()

    - **线程间的通信**:在多线程环境中,通过`wait()`和`notify()`/`notifyAll()`方法可以实现线程之间的协作。 - **生产者-消费者模式**:在经典的生产者-消费者模型中,生产者生产数据,消费者消费数据。当缓冲区满...

    wait和notifyAll方法的使用示例

    在Java多线程编程中,`wait()` 和 `notifyAll()` 方法是非常重要的同步机制,主要用于实现线程间的通信与同步控制。通过合理运用这些方法,可以有效地解决生产者消费者模型中的资源竞争问题。本文将结合具体的代码...

    一个理解wait()与notify()的例子

    本文旨在解析一个具体的Java多线程示例代码,以帮助读者更好地理解`wait()`与`notify()`方法的作用及其实现机制。这两个方法是Java中实现线程间通信的重要手段之一,尤其在解决生产者消费者模型、读者写者问题等经典...

    多线程sleep,yield,wait区别

    `sleep`, `yield`, 和 `wait` 是 Java 中用于线程管理的三个重要方法,它们各自有着不同的功能和用途。理解它们的区别有助于编写出更加高效和可控的多线程程序。 1. **sleep() 方法** - `Thread.sleep(millisecond...

    sleep() 方法和wait()方法的区别.docx

    sleep() 方法和 wait() 方法都是 Java 中的线程控制方法,但是它们有着本质的区别。下面是对这两种方法的详细比较和分析。 sleep() 方法 sleep() 方法是 Thread 类的静态方法,它的主要作用是使当前线程进入停滞...

    详解Java中wait和sleep的区别

    "详解Java中wait和sleep的区别" Java中wait和sleep的区别是Java多线程编程中一个重要的概念。这两个方法都可以用来暂停当前线程的执行,但是它们之间有着根本的区别。 首先,wait()是一个用于线程同步的实例方法...

    详解Java中的sleep()和wait()的区别

    在Java编程语言中,`sleep()` 和 `wait()` 都是用来控制线程执行的重要方法,但它们的作用和使用场景有着显著的区别。以下是这两个方法的详细解释: 1. **`Thread.sleep(long milliseconds)`** - `sleep()` 方法是...

    java sleep和wait的解惑.docx

    在Java多线程编程中,`Thread.sleep()` 和 `Object.wait()` 是两个非常重要的方法,它们分别位于 `Thread` 类和 `Object` 类中,用于控制线程的行为。本文将深入探讨这两个方法的区别以及它们在实际开发中的应用场景...

    java多线程.pdf

    Java提供了多种同步机制来控制多个线程对共享资源的互斥访问,包括synchronized关键字和Object类中的wait、notify和notifyAll方法。synchronized关键字可以用来修饰方法或者代码块,确保同一时刻只有一个线程可以...

    Java线程中wait,await,sleep,yield,join用法总结.pdf

    wait()、notify()和notifyAll()是Object类的方法,用于控制线程在特定条件下暂停执行和恢复执行。 - wait():当一个线程调用某个对象的wait()方法时,当前线程会释放对象锁,并进入该对象的等待队列。线程需要在...

    JAVA线程sleep()和wait()详解及实例

    JAVA线程sleep()和wait()详解及实例 JAVA线程sleep()和wait()是两个常用的线程控制方法,用于控制线程的执行和同步。本篇文章将详细介绍JAVA线程sleep()和wait()的原理、区别和实现机制,并提供实例代码以便更好地...

    Java多线程与并发系列22道高频面试题(附思维导图和答案解析)

    本文总结了Java多线程与并发系列的22道高频面试题,涵盖了多线程的实现方法、线程停止、notify和notifyAll的区别、sleep和wait的区别、volatile关键字的作用等多方面的知识点。 一、Java中实现多线程的方法 Java中...

    Java线程中sleep和wait的区别详细介绍

    4. `sleep()`需要捕获`InterruptedException`,而`wait()`, `notify()`, 和 `notifyAll()`不需要。 线程调度: 线程调度器根据线程的优先级决定哪个线程应该执行。它采用抢先式调度,如果高优先级线程变为可运行...

    java中yieldsleep以及wait的区别.pdf

    Java中的多线程编程涉及到许多关键概念,包括`yield()`, `sleep()`, 和 `wait()`,这些都是控制线程执行的重要方法。理解它们的区别对于编写高效并发程序至关重要。 首先,`yield()`方法的作用是让当前正在执行的...

    java 中sleep() 和 wait() 的对比

    在Java编程中,`sleep()` 和...在实际应用中,`sleep()` 常用来做简单的定时或者避免过快的循环,而 `wait()` 和 `notify()`(或 `notifyAll()`) 通常用于实现更复杂的同步机制,如生产者-消费者模型或哲学家就餐问题。

    Java多线程通信wait()和notify()代码实例

    "Java多线程通信wait()和notify()代码实例" Java多线程通信是Java编程语言中一个重要的概念,它允许程序同时执行多个线程,以提高程序的效率和响应速度。在Java中,多线程通信主要通过wait()和notify()方法实现,...

    Sleep和Wait的区别.pdf

    在多线程编程中,`sleep`和`wait`是两个非常重要的方法,用于控制线程的执行和协调多个线程之间的交互。理解这两个方法的不同之处对于正确实现多线程程序至关重要。 #### 二、`sleep`方法详解 ##### 1. 方法定义 `...

Global site tag (gtag.js) - Google Analytics