目录
一、线程之间的协作
在多线程中,经常会出现这种情况:一个线程改变了某个变量的值,而另一个线程感受到这个变量的值发生了变化,从而继续做某些事情
通常我们称前面一个线程为生产者(通知方),后面一个线程为消费者(等待方)
那么问题来了,消费者如何知道生产者是否改变了变量值呢?
一种简单粗暴的方式就是轮询,消费者不断去检查该变量的值。缺点也显而易见:缺乏及时性,浪费资源
这时,我们可以采用“等待通知”机制来实现这个要求。在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
---------------------------------------------------------------------------------------------------------------------------------------------------
如果我的文章对您有点帮助,麻烦点个赞,您的鼓励将是我继续写作的动力
如果哪里有不正确的地方,欢迎指正
如果哪里表述不清,欢迎留言讨论