代码
package com.xiayc.waitnotify;
import java.util.ArrayList;
import java.util.List;
/**
* 1、wait、notify和notifyAll方法是Object类提供的,换句话说Java中所有的对象都有这三个方法;
* 2、wait、notify和notifyAll方法要配合synchronized关键字同步操作才有意义;
* 3、notify只是选择一个wait状态线程进行通知,并使它获得该对象上的锁,但不惊动其他同样在等待被该对象notify的线程们,
* 当第一个线程运行完毕以后释放对象上的锁此时如果该对象没有再次使用notify语句,则即便该对象已经空闲,
* 其他wait状态等待的线程由于没有得到该对象的通知,继续处在wait状态,直到这个对象发出一个notify或notifyAll;
* 4、notifyAll使所有原来在该对象上等待被notify的线程统统退出wait的状态,变成等待该对象上的锁,一旦该对象被解锁,他们就会去竞争;
* 5、wait操作释放锁,notify和notifyAll不释放锁;
*
* @author xyc
*
*/
public class WaitAndNotify {
/**
* volatile用于线程之间互相可见
* 主要使t3线程可见list的改变
* 因为synchronized关键字可以保证可见性,所以t1、t2、t4线程不需要担心
*/
private volatile static List<Integer> list = new ArrayList();
/**
* 创建一个对象作为对象锁
*/
private static Object lock = new Object();
public static void main(String[] args) {
/**
* t1线程第一个start执行
* 1、获得对象锁lock;
* 2、此时t2和t4线程可能已经执行,但是t1已经获得对象锁,所以它们只有等待着;
* 3、接着判断list的size是否为5,此时由于t2线程一直被阻塞所以未执行添加代码,故list的size等于0;
* 4、所以执行对象锁对象lock的wait方法;
* 5、执行wait方法会释放当前对象锁,且t1线程阻塞在当前位置,等待lock的notify唤醒;
*/
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
if(list.size()!=5) {
try {
System.out.println("t1被阻塞,等待唤醒");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t1 do something...");
}
}
},"t1").start();
/**
* t2线程紧接着t1线程执行
* 1、由于t1线程首先获得lock对象锁,故一直在等待执行;
* 2、t1线程执行wait语句后释放了lock对象锁,此时t2线程开始执行;
* 3、list循环添加元素,如果i==4则执行notify唤醒操作;
* 4、虽然执行了notify操作但notify方法不会释放对象锁,所以t2线程继续执行至结束,而t1线程继续阻塞;
* 5、t2线程结束后释放了对象锁,此时t1线程才会被唤醒,打印t1 do something...语句;
*/
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add(i);
if(i==4) {
System.out.println("t2 notify...");
lock.notify();
}
}
}
}
},"t2").start();
/**
* t3线程提供的解决方案相比t1来说虽然可以解决问题,但更加的消耗性能,并不可取;
*/
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
if(list.size()==5) {
System.out.println("t3 do something...");
break;
}
}
}
},"t3").start();
/**
* t4线程和t1线程唯一的区别就是t1在t2线程之前执行,而t4线程是在t2线程之后执行,
* 所以t2先于t4执行并获得对象锁,而t2执行完之后才执行t4,而此时list的size已经是100,
* 所以t4也会执行wait操作,但没有其它的线程来唤醒,所以一直阻塞在这里形成死锁;
*/
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
if(list.size()!=5) {
try {
System.out.println("t4被阻塞,等待唤醒");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t4 do something...");
}
}
},"t4").start();
}
}
执行结果
t1被阻塞,等待唤醒
t2 notify...
t3 do something...
t1 do something...
t4被阻塞,等待唤醒