Object.wait()、Object.notify()、Object.notifyAll()必须要与synchronized一起使用,必须在synchronized方法内部或synchronized块内部使用。
wait
让当前线程进入等待(阻塞)WAITTING 状态,将线程放入到等待队列中,并释放对象锁。
需要注意:调用wait方法后,会立即释放当前线程锁占有的对象锁。会暂停执行wait后面的代码块,需要等待,其它拿到该锁对象的线程执行notify或notifyAll方法唤醒该线程后,才会继续执行wait后面的代码块。
三个wait方法的对比:
-
wait()
- 不会自动被唤醒,需等其他占有该对象锁的线程调用锁对象的notify或notifyAll方法时才会被唤醒
-
wait(long timeout)
- timeout(毫秒)时间后会被自动唤醒
- 其他占有该对象锁的线程调用锁对象的notify或notifyAll方法唤醒
-
wait(long timeout, int nanos)
- nanos的范围为:0-999999
- 当nanos=0时,timeout(毫秒)时间后会被自动唤醒;
- 当0<nanos<=999999时,timeout+1(毫秒)时间后会被自动唤醒
- 其他占有该对象锁的线程调用锁对象的notify或notifyAll方法唤醒
- nanos的范围为:0-999999
wait(0)方法
wait(0)等同于wait()。
notify
从当前对象锁的等待队列中获取一个线程(如果有多个线程则获取优先级高的,优先级都一样则随机),移入到同步队列中,参与锁的竞争。
需要注意:调用notify方法后,当前线程不会马上释放该对象锁,要等到同步块或者同步方法执行完后,当前线程才会释放锁。
notifyAll
和notify一样,都是用来唤醒处于等待状态的线程参与锁的竞争,但也有一些区别。
notifyAll会将当前对象锁的等待队列的所有线程,都移入到同步队列中,但与锁的竞争。
需要注意:调用notifyAll方法后,当前线程不会马上释放该对象锁,要等到同步块或者同步方法执行完后,当前线程才会释放锁。
锁的释放问题
- wait会立即释放当前线程锁占有的对象锁;
- notify和notifyAll不会立即释放,会等到当前的同步块或同步方法执行完后,才会释放当前线程锁占有的对象锁。
经典示例
例:使用两个线程,顺序打印1-100的数字。
public static void main(String[] args) {
Object lock = new Object();
AtomicInteger a = new AtomicInteger(1);
Thread t1 = new Thread(() -> {
synchronized (lock) {
for (; a.get() <= 100; ) {
lock.notify();
System.out.println("Thread t1:" + a.get());
a.getAndIncrement();
if (a.get() >= 100) {
break;
}
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread t2 = new Thread(() -> {
synchronized (lock) {
for (; a.get() <= 100; ) {
lock.notify();
System.out.println("Thread t2:" + a.get());
a.getAndIncrement();
if (a.get() >= 100) {
break;
}
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t1.start();
t2.start();
}