一、前言
在上篇博客中介绍了synchronized的常见操作,不过还有一点没有介绍到,那就是关于线程的等待和唤醒,因此本篇就介绍这两个方法
二、由来
在介绍wait、notify方法之前,我们需要知道为什么会有这个方法。在Java中,每个对象都有个对象锁标志(Object lock flag)与之相关联,当一个线程A调用对象的一段synchronized代码时,
它首先要获取与这个对象关联的对象锁标志,然后执行相应的代码,执行结束后,把这个对象锁标志返回给对象;因此,在线程A执行
synchronized代码期间,此时在 synchronized的方法中调用了sleep方法,如果另一个线程B也要执行同一对象的一段synchronized代码时(不一定与线程A执行的相同),它将 要等到线程A执行完后,才能继续。这样就造成了阻塞。
- wait:导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll()方法.wait会释放占有的锁,notify和notifyAll不会释放占用的锁.
- void notifyAll():唤醒在此对象监视器上等待的所有线程。
- void notify():唤醒在此对象监视器上等待的某一个线程.如果有多个,具体是哪一个可以认为是不确定的.
注意:以上三个方法都是本地方法,不可以被重写
三、Demo
public class WaitNotifyTest {
public static Class lock = WaitNotifyTest.class;
public static void main(String[] args) throws Exception {
new TestThread2().start();
new TestThread2().start();
Thread.sleep(3000);
synchronized (WaitNotifyTest.lock) {
try {
System.out.println(Thread.currentThread().getName() + " sent notification all");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
class TestThread2 extends Thread {
public void run() {
synchronized (WaitNotifyTest.lock) {
System.out.println(Thread.currentThread().getName() + " wait for notification");
try {
WaitNotifyTest.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " wake up");
for (int i = 0; i < 3; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " doing " + i);
}
}
}
在上述代码中,首先开启两个线程,并且在run方法里调用全局琐的wait方法,接着输出如下:
Thread-0 wait for notification
Thread-1 wait for notification
main sent notification all
注意:此时程序并没有退出,因为两个线程都调用了wait方法,导致当前的线程等待。那么怎么可以唤醒wait方法的线程。接着看:
public static void main(String[] args) throws Exception {
new TestThread2().start();
new TestThread2().start();
Thread.sleep(3000);
synchronized (WaitNotifyTest.lock) {
try {
System.out.println(Thread.currentThread().getName() + " sent notification all");
WaitNotifyTest.lock.notifyAll();
} catch (Exception e) {
e.printStackTrace();
}
}
}
只需要在main方法里面添加notifyAll(),输出如下:
Thread-0 wait for notification
Thread-1 wait for notification
main sent notification all
Thread-1 wake up
Thread-0 wake up
Thread-1 doing 0
Thread-0 doing 0
Thread-0 doing 1
Thread-1 doing 1
Thread-0 doing 2
Thread-1 doing 2
此时,程序完整退出。notifyAll()唤醒所有线程。
注意:我们使用什么锁,就需要使用什么锁来让其线程等待和唤醒,否则会报错。
java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.lw.thread.dielock.TestThread2.run(WaitNotifyTest.java:33)
一个线程被唤醒不代表立即获取了对象的monitor,只有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁执行。
四、总结
本篇主要介绍了wait、nofity等方法的作用和注意事项。下一篇将会介绍lock方面的