探索JAVA并发 - 终于搞懂了sleep/wait/notify/notifyAll
来源:互联网
> sleep/wait/notify/notifyAll分离有甚么作用?它们的区分是甚么?wait时为何要放在重复里而不能直接用if?
## 简介
首先对几个相干的办法做个简略说明,Object中有几个用于线程同步的办法:wait、notify、notifyAll。
```java
public class Object {
public final native void wait(long timeout) throws InterruptedException;
public final native void notify();
public final native void notifyAll();
}
```
+ wait: 释放当前锁,阻塞直到被notify或notifyAll唤醒,或超时,或线程被中止(InterruptedException)
+ notify: 任意选择一个(没法掌握选哪一个)正在这个对象上期待的线程把它唤醒,其它线程仍然在期待被唤醒
+ notifyAll: 唤醒所有线程,让它们去竞争,不过也只有一个能抢到锁
+ sleep: 不是Object中的办法,而是Thread类的静态办法,让当前线程持有锁阻塞指定时光
## sleep和wait
sleep和wait都可让线程阻塞,也都可以指定超时时光,乃至还都会抛出中止异常InterruptedException。
而它们最大的区分就在于,sleep时线程仍然持有锁,他人没法进当前同步办法;wait时废弃了持有的锁,其它线程有机遇进入该同步办法。屡次提到同步办法,由于wait必需在synchronized同步代码块中,否则会抛出异常IllegalMonitorStateException,notify也是如此,可以说wait和notify是就是为了在同步代码中做线程调度而生的。
下面一个简略的例子展示sleep和wait的区分:
```java
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
// 日志行号记载
private AtomicInteger count = new AtomicInteger();
public static void main(String[] args) throws InterruptedException {
Main main = new Main();
// 开启两个线程去履行test办法
new Thread(main::test).start();
new Thread(main::test).start();
}
private synchronized void test() {
try {
log("进入了同步办法,并开端睡觉,1s");
// sleep不会释放锁,因此其他线程不能进入这个办法
Thread.sleep(1000);
log("睡好了,但没事做,有事叫我,期待2s");
//阻塞在此,并且释放锁,其它线程可以进入这个办法
//当其它线程调用此对象的notify或notifyAll时才有机遇停止阻塞
//就算没有人notify,如果超时了也会停止阻塞
wait(2000);
log("我要走了,但我要再睡一觉,10s");
//这里睡的时光很长,由于没有释放锁,其它线程就算wait超时了也没法持续履行
Thread.sleep(10000);
log("走了");
notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 打印日志
private void log(String s) {
System.out.println(count.incrementAndGet() + " "
+ new Date().toString().split(" ")[3]
+ "\t" + Thread.currentThread().getName() + " " + s);
}
}
/* 输出:
1 00:13:23 Thread-0 进入了同步办法,并开端睡觉,1s
2 00:13:24 Thread-0 睡好了,但没事做,有事叫我,期待2s
3 00:13:24 Thread-1 进入了同步办法,并开端睡觉,1s
4 00:13:25 Thread-1 睡好了,但没事做,有事叫我,期待2s
5 00:13:26 Thread-0 我要走了,但我要再睡一觉,10s
6 00:13:36 Thread-0 走了
7 00:13:36 Thread-1 我要走了,但我要再睡一觉,10s
8 00:13:46 Thread-1 走了
*/
```
对输出做个简略说明(已看懂代码的童鞋可以跳过):
```
1 00:13:23 Thread-0 进入了同步办法,并开端睡觉,1s // Thread-0首先进入同步办法,Thread-1只能门外候着
2 00:13:24 Thread-0 睡好了,但没事做,有事叫我,期待2s // Thread-0 sleep 1秒这段时光,Thread-1没进来,证明sleep没有释放锁
3 00:13:24 Thread-1 进入了同步办法,并开端睡觉,1s // Thread-0开端wait后Thread-1马上就进来了,证明wait释放了锁
4 00:13:25 Thread-1 睡好了,但没事做,有事叫我,期待2s // Thread-1也盘算wait 2秒(2秒后真的能醒来吗?)
5 00:13:26 Thread-0 我要走了,但我要再睡一觉,10s // Thread-0已wait超时醒来了,这次预备sleep 10s
6 00:13:36 Thread-0 走了 // 10s过去了Thread-0都sleep停止了,那个说要wait 2s的Thread-1还没消息,证明超时也没用,还得抢到锁
7 00:13:36 Thread-1 我要走了,但我要再睡一觉,10s // Thread-0退出同步代码后,Thread-1才终究得到了锁,能行为了
8 00:13:46 Thread-1 走了
```
## notify和notifyAll
一样是唤醒期待的线程,一样最多只有一个线程能取得锁,一样不能掌握哪一个线程取得锁。
区分在于:
+ notify:唤醒一个线程,其他线程仍然处于wait的期待唤醒状况,如果被唤醒的线程停止时没调用notify,其他线程就永久没人去唤醒,只能期待超时,或被中止
+ notifyAll:所有线程退出wait的状况,开端竞争锁,但只有一个线程能抢到,这个线程履行完后,其他线程又会有一个荣幸儿脱颖而出得到锁
如果认为说明的不够明确,代码来一波:
```java
import java.util.Date;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
private AtomicInteger count = new AtomicInteger();
public static void main(String[] args) throws InterruptedException {
Main main = new Main();
// 开启两个线程去履行test办法
for (int i = 0; i
> sleep/wait/notify/notifyAll分离有甚么作用?它们的区分是甚么?wait