线程间通信之wait/notify

       线程开始运行,拥有自己的栈空间,就会按照既定的代码一步一步运行直到终止。如果每个运行中的线程仅仅是孤立地运行,那么没有一点价值,或者说价值很少,如果多个线程能够相互配合完成工作,这将会带来巨大的价值

                                                                                                                --------《Java 并发编程的艺术》

———————————————————————————————————————————————————

本文主要介绍线程间通信的通知/等待机制:wait()、notify()/notifyAll()

       一个线程修改了一个对象的值,而另一个线程感知到了变化,然后进行相应的操作,整个过程开始于一个线程,而最终执行的又是另一个线程。应用到等待/通知机制,可以理解为:一个线程A调用的对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而执行后续操作。上述两个线程通过对象O来完成交互,而对象上的wait()和notify/notifyAll()的关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作。

划重点:wait()和notify/notifyAll()是对象的方法,是任意Java对象都具备的,也就是说这些方法都是定义在所有对象的超类java.lang.Object上。


我们先对这些方法做如下描述:

notify():通知一个在对象上等待的线程,使其从wait()方法返回,而返回的前提是该线程获得了对象的锁;

notifyAll():通知所有等待在该对象上的线程;

wait():调用该方法的线程进入WAITING状态,只有等待另外线程的通知或被中断才会返回,需要注意,调用wait()法后,会释放对象的锁;

wait(long n):超时等待一段时间,这里的参数时间是毫秒,也就是说,等待n毫秒,如果没有通知就超时返回。

       接下来,我借助一个例子来深入分析通知/等待机制,两个线程WaitThread和NotifyThread,前者检查flag是否为false,如果符合要求,进行后续操作,否则在lock上等待;后者在睡眠了一段时间后对lock进行通知。

public class WaitNotify{
	static boolean flag = true;
	static Object lock = new Object();

	public static void main(String[] args){
		Thread waitThread = new Thread(new wait(),"WaitThread");
		waitThread.start();
		TimeUnit.SECONDS.sleep(1);
		Thread notifyThread = new Thread(new notify(),"NotifyThread");
		notifyThread.start();
	}
	static class wait implements Runnable{
		public void run(){
			synchronized(lock){
				while(flag){
					try{
						System.out.println(Thread.currentThread() + "flag is true. wait @ " 
							+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
						lock.wait();
					}catch (InterruptedException e){
					}
				}
				System.out.println(Thread.currentThread() + "flag is false. running @ " 
					+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
			}
		}
	}
	static class notify implements Runnable{
		public void run(){
			synchronized(lock){
				System.out.println(Thread.currentThread() + "hold lock. notify @ " 
					+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
				lock.notifyAll();
				flag = false;
				SleepUtils.second(5);
			}
			synchronized(lock){
				System.out.println(Thread.currentThread() + "hold lock again. notify @ " 
					+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
				SleepUtils.second(5);
			}
		}
	}
}

输出如下:


其中SleepUtils是自己创建的线程休眠工具类。

上述输出第3行和第4行顺序可能会互换,通过这个例子主要是想说明调用wait()、notify()和notifyAll()时要注意的细节:

1、使用wait()、notify()和notifyAll()时需要先对调用对象加锁;

2、调用wait()方法后,线程状态由RUNNING变为WAITING,并将当前线程放入对象的等待队列中;

3、notify()或notifyAll()方法调用后,等待线程依旧不会从wait()返回,需要调用notify()或notifyAll()的线程释放锁后,等待线程才有机会从wait()返回;

4、notify()方法将等待队列中的一个等待线程从等待队列移到同步队列中,而notifyAll()方法是把等待队列中的所有线程移动到同步队列中,被移动的线程状态由WAITING变为BLOCKED;

5、从wait()方法返回的前提是获得了调用对象的锁。

       从以上细节可以看出,等待/通知机制依托于同步机制,其目的就是确保等待线程从wait()方法返回时能够感知到通知线程对变量做出的修改。

可以总结出等待/通知的一般模式:

等待方遵循的原则:1)获取对象的锁;2)如果条件不满足,那么调用对象wait()方法,被通知后仍然要检查条件;3)条件满足则执行对应的逻辑。

通知方遵循的原则:1)获得对象的锁;2)改变条件;3)通知等待在对象上的线程;

以伪代码的形式给出:

等待方:

synchronized(对象){
    while(条件不满足){
        对象.wait();
    }
    对应的逻辑处理
}

通知方:

synchronized(对象){
    改变条件;
    对象.notify()/notifyAll();
}

小伙伴们有没有get到用boolean变量作为线程挂起与恢复的标识位的方法呢?

————————————————————————————————————————————————————

参考:方腾飞 《Java并发编程的艺术》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值