Java并发基础(4)——“等待通知”和“等待唤醒”

目录

一、线程之间的协作

二、“等待通知”机制

2.1 demo

三、“等待唤醒”机制

3.1 demo


一、线程之间的协作

在多线程中,经常会出现这种情况:一个线程改变了某个变量的值,而另一个线程感受到这个变量的值发生了变化,从而继续做某些事情

通常我们称前面一个线程为生产者(通知方),后面一个线程为消费者(等待方)

那么问题来了,消费者如何知道生产者是否改变了变量值呢?

一种简单粗暴的方式就是轮询,消费者不断去检查该变量的值。缺点也显而易见:缺乏及时性,浪费资源

这时,我们可以采用“等待通知”机制来实现这个要求。在Java中,我们常用 synchronized 配合 wait()、notify()、notifyAll() (推荐)这三个方法就能实现该机制。

二、“等待通知”机制

“等待通知”机制的标准范式

等待方通知方
1、拿到对象的锁1、拿到对象的锁
2、循环里判断条件是否满足
              不满足则等待
2、改变条件
3、条件满足执行业务3、通知所有等待的线程

2.1 demo

public class People {
	
	private Integer height;//身高
	
	public People() {
	}

	public People(Integer height) {
		super();
		this.height = height;
	}

	//改变身高
	public synchronized void changeHeight() {
		this.height = 180;
		notifyAll();
	}
	
	//等待身高变化
	public synchronized void waitHeight() {
		while (this.height < 180) {
			try {
				System.out.println("waitHeight ["+Thread.currentThread().getId() +"] 进入等待状态.");
				wait();
				System.out.println("waitHeight ["+Thread.currentThread().getId() +"] 被唤醒.");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("waitHeight ["+Thread.currentThread().getId() +"] 执行完毕,此时身高:" + this.height);
	}
}

测试代码

public class TestWaitAndNotify {

    private static People people = new People(100);
	
    //等待方
	private static class CheckHeight extends Thread{
		@Override
		public void run() {
			people.waitHeight();
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		for(int i = 0;i < 3;i++){
			new CheckHeight().start();
		}
		
		Thread.sleep(1000);
		//通知方
		people.changeHeight();
	}
}

我们在main方法中新起了三个线程

这三个线程通过竞争,先拿到锁的线程执行waitHeight()

当执行到wait()时会进入等待状态并释放掉锁,另外两个线程竞争,同理,直到三个线程都在等待

等主线程执行people.changeHeight();时,会唤醒所有在等待的线程,继续执行自己的业务

运行结果如下

注意这里使用了notifyAll() 唤醒所有等待的线程

如果使用notify(),随机唤醒一个,如果有其他条件的线程在等待,可能也会被唤醒,唤醒后检查条件不符合后又继续等待

三、“等待唤醒”机制

在上面示例中,进入等待状态的线程会一直等待,直到唤醒。这显然不符合实际场景。

接下来学习“等待超时”机制

“等待超时”机制的标准范式:

当前时间now

等待时间T

当超过now+T时间后超时overtime

剩余时间为retime

while(判断条件不满足  && retime> 0){

       wait(retime);//等待

       retime= overtime - now;//更新剩余时间

}

3.1 demo

public class People {
	
	private Integer height;//身高
	
	public People() {
	}

	public People(Integer height) {
		super();
		this.height = height;
	}

	//改变身高
	public synchronized void changeHeight() {
		this.height = 180;
		notifyAll();
	}
	
	//等待身高变化
	public synchronized void waitHeight(long mills) throws InterruptedException {
		if(mills < 0){
			//mills < 0  永不超时
			System.out.println("waitHeight ["+Thread.currentThread().getId() +"] 进入等待状态.");
			wait();
			System.out.println("waitHeight ["+Thread.currentThread().getId() +"] 被唤醒.");
		}else {
			//超时时间
			long overtime = System.currentTimeMillis() + mills;
			//剩余时间
			long retime = mills;
			//判断连接池中是否有连接且剩余时间是否大于0
			while(this.height < 180 && retime > 0){
				System.out.println("waitHeight ["+Thread.currentThread().getId() +"] 进入等待状态.");
				wait(retime);
				System.out.println("waitHeight ["+Thread.currentThread().getId() +"] 被唤醒.");
				//如果被唤醒,则要更新剩余时间
				retime = overtime - System.currentTimeMillis();
			}
			
		}
		System.out.println("waitHeight ["+Thread.currentThread().getId() +"] 执行完毕,此时身高:" + this.height);
	}
}
public class TestWaitAndNotify {

    private static People people = new People(100);
	
    //等待方
	private static class CheckHeight extends Thread{
		@Override
		public void run() {
			try {
                //修改等待时间,可以看到不同结果
				people.waitHeight(-1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		new CheckHeight().start();
		Thread.sleep(2000);
		//通知方
		people.changeHeight();
	}
}

通过修改等待时间,可以看到不同的结果

如果在等待时间内,通知方执行people.changeHeight(); 修改身高,则满足等待方的条件,最终身高则是180

如果等待时间短,则等待方超时后,就结束循环,什么也不做,最终身高还是100

---------------------------------------------------------------------------------------------------------------------------------------------------

如果我的文章对您有点帮助,麻烦点个赞,您的鼓励将是我继续写作的动力

如果哪里有不正确的地方,欢迎指正

如果哪里表述不清,欢迎留言讨论

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值