/**
* 线程之间的等待/通知机制
*
* @author Shawn Wong
*
*/
public class WaitNotifyThread {
private static Object lock = new Object();
private static volatile boolean flag = true;
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
// 等待线程A
Thread a = new Thread(new WaitThread(), "thread-A");
a.start();
// 等待线程B
Thread b = new Thread(new WaitThread(), "thread-B");
b.start();
// 通知线程C
Thread c = new Thread(new NofifyThread(), "thread-C");
c.start();
}
static class WaitThread extends Thread {
@Override
public void run() {
test();
}
void test() {
synchronized (lock) {
while (flag) {
System.out.println(Thread.currentThread().getName() + " enter loop");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " notify");
}
}
}
}
static class NofifyThread extends Thread {
@Override
public void run() {
test();
}
void test() {
synchronized (lock) {
System.out.println("start to notify all wait ");
lock.notifyAll();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = false;
System.out.println("enter ");
}
}
}
}
运行结果:
流程图:
1、 在对象Lock上添加Synchronized关键字,锁会被记录在Lock的对象头中,并在被锁的程序块的前后分别添加了monitor.enter 和monitor.exit的字节码, 程序走到monitor.exit后会立即释放锁,其他线程才能再次获取到锁,从而进入到代码块中,保证了顺序性(一致性)和可见性。
2、如果线程A在monitor.enter成功后,但没有走完monitor.exit的字节码,线程B此时访问被锁的代码块,monitor.enter会失败,线程B的状态会自动变成BLOCKED,进入阻塞队列中。一旦线程A走完monitor.exit的字节码,线程B会有可能获取到锁,进入代码块。
3、线程A在进入wait()方法时,会自动进入等待队列中,并且释放掉锁,等待其他线程的通知。 由于已经释放了锁,通知线程C就可以获取到锁,并执行对象的notify方法,唤醒进入等待队列中的线程A,只有线程C完全释放掉锁后,等待队列中的线程才会迁移到同步阻塞队列中,即线程C的状态从WAITING变成BLOCKED,再去获取锁。
参考书籍: 《Java并发编程的艺术》