Java线程的等待与唤醒主要包括几个方法:
(1)notify():唤醒在此对象监视器上等待的单个线程。
(2)notifyAll():唤醒在此对象监视器上等待的所有线程。
(3)wait():让当前线程处于阻塞状态,同时释放它所持有的锁。
(4)wait(long timeout):让线程处于阻塞状态,直到其他线程调用此对象的notify()或者notifyAll()方法,或者超过指定的时间量,当前线程被唤醒。
(5)wait(long timeout,int nanos):同上。
wait()和notify()函数示例:
- class ThreadA extends Thread{
- public ThreadA(String name) {
- super(name);
- }
- public void run() {
- synchronized (this) {
- System.out.println(Thread.currentThread().getName()+" call notify()");
- // 唤醒当前的wait线程
- notify();
- }
- }
- }
- public class WaitTest {
- public static void main(String[] args) {
- ThreadA t1 = new ThreadA("t1");
- synchronized(t1) {
- try {
- // 启动“线程t1”
- System.out.println(Thread.currentThread().getName()+" start t1");
- t1.start();
- // 主线程等待t1通过notify()唤醒。
- System.out.println(Thread.currentThread().getName()+" wait()");
- t1.wait();
- System.out.println(Thread.currentThread().getName()+" continue");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
运行结果:
main start t1 main wait() t1 call notify() main continue
对于结果可能有些人不理解第二个输出(main wait),说明此时main线程还在运行着,
其实t1.wait()的意思含义是停止当前的线程,当然就是CPU上正在运行的程序,这样整个的结果输出就比较合理,t1在运行run()函数会运行notify(),main线程开始重新运行。
wait(long timeout)和notify()函数:
示例程序:
- // WaitTest.java的源码
- class ThreadA extends Thread{
- public ThreadA(String name) {
- super(name);
- }
- public void run() {
- System.out.println(Thread.currentThread().getName() + " run ");
- // 死循环,不断运行。
- while(true);
- }
- }
- public class WaitTimeoutTest {
- public static void main(String[] args) {
- ThreadA t1 = new ThreadA("t1");
- synchronized(t1) {
- try {
- // 启动“线程t1”
- System.out.println(Thread.currentThread().getName() + " start t1");
- t1.start();
- // 主线程等待t1通过notify()唤醒 或 notifyAll()唤醒,或超过3000ms延时;然后才被唤醒。
- System.out.println(Thread.currentThread().getName() + " call wait ");
- t1.wait(3000);
- System.out.println(Thread.currentThread().getName() + " continue");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
运行结果:
main start t1
main call wait
t1 run // 大约3秒之后...输出“main continue”
main continue
在运行t1.wait(3000),main线程会让出3秒CPU,之后会重新竞争资源。
wait()和notifyAll()方法
示例代码:
- package test;
- public class NotifyAllTest {
- private static Object obj = new Object();
- public static void main(String[] args) {
- ThreadA t1 = new ThreadA("t1");
- ThreadA t2 = new ThreadA("t2");
- ThreadA t3 = new ThreadA("t3");
- t1.start();
- t2.start();
- t3.start();
- try {
- System.out.println(Thread.currentThread().getName()+" sleep(3000)");
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- synchronized(obj) {
- // 主线程等待唤醒。
- System.out.println(Thread.currentThread().getName()+" notifyAll()");
- obj.notifyAll();
- }
- }
- static class ThreadA extends Thread{
- public ThreadA(String name){
- super(name);
- }
- public void run() {
- synchronized (obj)
- {
- try {
- // 打印输出结果
- System.out.println(Thread.currentThread().getName() + " wait");
- // 唤醒当前的wait线程
- obj.wait();
- // 打印输出结果
- System.out.println(Thread.currentThread().getName() + " continue");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
第一次运行结果:
t2 wait
main sleep(3000)
t1 wait
t3 wait
main notifyAll()
t3 continue
t1 continue
t2 continue
第二次运行结果:
main sleep(3000)
t2 wait
t3 wait
t1 wait
main notifyAll()
t1 continue
t3 continue
t2 continue
刚开始每个线程都运行wait将其他线程阻塞,最后main线程重新获得资源,运行notify()函数,唤醒所有的线程。
为什么notify(),wait()等函数定义在Object中,而不是Thread类中。
Object中的wait(),notify()等函数,和synchronized一样,会对对象的同步锁进行操作。wait()会使当前线程处于阻塞状态,是因为该线程进入了阻塞状态,所以线程应该释放了它拥有的同步锁,否则其他线程无法运行。wait()等待线程和notify()之间是通过什么关联起来的?答案是对象的同步锁。负责唤醒等待线程的那个线程,只有它在获取了该对象的同步锁之后,才调用notify或notify()方法之后,才能唤醒等待线程。
总之,notify(),wait()依赖于同步锁,而同步锁是对象锁持有,并且每个对象有且仅有一个,这就是为什么notify()和wait()等函数定义在object类,而不是Thread类中的原因。