等待(wait) 和 通知(notify)
说明: 文本是学习了《Java 高并发程序设计》中的等待和通知内容的学习笔记,结合实例总结了一些小结论。
wait () 方法顾名思义就是线程处于等待状态,当程序执行遇到synchronize同步块时,线程会进入BLOCKED阻塞状态,此时线程暂时停止执行知道获取到请求锁。当调用wait方法时,线程就会进入WAITING状态,知道等待到一个notify方法,线程就会重新执行。提到wait,join方法也是等待方法,两者的区别是wait等到notify就会继续执行,而join会等到目标线程终止。
wait和notify的使用,这两个方法不是Thread类中的方法,而是输入的Object对象的方法,如果线程A调用了object.wait,那么线程A会处于等待状态,直到其他线程调用了object.notify,A才会继续执行。
下面是一个简单的小例子:
private static Object object = new Object();
public static class T1 extends Thread {
@Override
public void run() {
synchronized (object) {
try {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getId() + " T1 start ");
System.out.println("T1 wait 1");
object.wait();
} catch (InterruptedException e) {
System.out.println("T1 InterruptedException");
}
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getId() +" T1 end!");
}
}
}
public static class T2 extends Thread {
@Override
public void run() {
synchronized (object) {
try {
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getId() + " T2 start ");
System.out.println(" T2 notify");
object.notify();
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println("T2 InterruptedException");
}
System.out.println(System.currentTimeMillis() + " : " + Thread.currentThread().getId() +" T2 end!");
}
}
}
public static void main(String[] args) throws Exception{
new T1().start();
new T2().start();
}
执行的结果是:
根据控制台输出的结果可以看出,当T2调用了object.notify()后,T1并没有立即执行,而是等待T2执行完之后,T1才获取到锁,重新继续执行。
如果在T1中调用两次wait,T2中notify两次,T1会end么?
public static class T1 extends Thread {
@Override
public void run() {
synchronized (object) {
try {
System.out.println(System.currentTimeMillis() + " : threadId:" + Thread.currentThread().getId() + " T1 start ");
System.out.println("T1 第一次wait 1");
object.wait();
System.out.println(System.currentTimeMillis() + " : T1 第二次 wait 2");
object.wait();
} catch (InterruptedException e) {
System.out.println("T1 InterruptedException");
}
System.out.println(System.currentTimeMillis() + " : threadId:" + Thread.currentThread().getId() +" T1 end!");
}
}
}
public static class T2 extends Thread {
@Override
public void run() {
synchronized (object) {
try {
System.out.println(System.currentTimeMillis() + " : threadId:" + Thread.currentThread().getId() + " T2 start ");
System.out.println("T2 第一次notify");
object.notify();
Thread.sleep(3000);
System.out.println("T2 第二次notify");
object.notify();
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("T2 InterruptedException");
}
System.out.println(System.currentTimeMillis() + " : threadId:" + Thread.currentThread().getId() +" T2 end!");
}
}
}
此时的结果如下:
如图所示,T1第一次wait是在T2结束之后被通知的,第二次wait一直处于等待状态。
如何将T1执行结束呢?
public static class T2 extends Thread {
@Override
public void run() {
synchronized (object) {
try {
System.out.println(System.currentTimeMillis() + " : threadId:" + Thread.currentThread().getId() + " T2 start ");
System.out.println("T2 第一次notify");
object.notify();
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println("T2 InterruptedException");
}
System.out.println(System.currentTimeMillis() + " : threadId:" + Thread.currentThread().getId() +" T2 end!");
}
}
}
public static void main(String[] args) throws Exception{
new T1().start();
new T2().start();
new T2().start();
// 或者第二中方式
new T1().start();
new T2().start();
Thread.sleep(5000);
synchronized (object) {
System.out.println(System.currentTimeMillis() + " object.notify()");
object.notify();
}
}
结果如下:
第二中方式的结果如下:
如图,当新创建一个T2时,T1可以继续执行知道End。
当T1调用object.wait 时,object的锁被释放,T2调用object.notify时获取到object的锁,当T2执行完之后释放了object锁,此时T1获取到了object的锁,继续程序。第二个例子中,将T1调用了两次wait,T2调用了两次notify,但是T1没有继续执行,而是停在了第二次wait,由于T2执行结束,所以T1第一次等待被唤醒,第二次等待由于T2已经完成所以第二次的wait获取不到notify的唤醒,一直处于等待状态。
第三个例子中,重新new了一个T2,此时的T2和第一次New的T2是两个完全不用的线程。从结果也可以看出,当id为13的线程结束是T1才结束,也就是说T1的第二个wait是id为13的线程唤醒的。