五、线程之间的协作
一、典型例子:生产者消费者
自行百度,不作赘述
二、业务场景
当A线程对一个变量进行修改,然后通知其他线程之后,B线程根据这个通知进行触发相应的业务逻辑。
最笨的方法:我们首先会在B线程中:
while(条件不满足){
check 条件不满足
sleep(50);
}
但是这种做法有个问题,不能保证业务变化的及时性,即可能我刚开始休眠这个条件就已经满足了还需要等待固定的时间,有人就说:那我可以把休眠时间调整的尽可能小,那么对于我们的CPU的负载压力就很大了,因此这种方式是有弊端的。
wait()
当A线程的O对象调用wait()方法之后,进入等待状态,只有当B线程调用O对象的notify()或notifyAll()方法之后才会唤醒A线程。A唤醒之后做自己的相关业务工作。
notify()和notifyAll()
如果某些线程在等待某些条件触发,那当那些条件为真时,你可以用 notify 和 notifyAll 来通知那些等待中的线程重新开始运行。不同之处在于,notify 仅仅通知一个线程,并且我们不知道哪个线程会收到通知,然而 notifyAll 会通知所有等待中的线程。换言之,如果只有一个线程在等待一个信号灯,notify和notifyAll都会通知到这个线程。但如果多个线程在等待这个信号灯,那么notify只会通知到其中一个,而其它线程并不会收到任何通知,而notifyAll会唤醒所有等待中的线程。
三、等待和通知的标准范式
等待方要进行等待时:
syn(对象){
while(条件不满足){
对象.wait()
}
条件满足了,做业务逻辑
}
通知方:
syn(对象){
改变条件
对象.notify()/notifyAll()
}
四、notify()和notifyAll()应该用谁
notify()具有随机性,因为不知道要唤醒哪一个,所以在不确定要唤醒具体哪个线程的时候,最好是用notifyAll(),谨慎使用notify()。
五、市面上所有的连接池都是通过等待超时模式实现的
一个连接池中有固定的(比如10个)连接数,如果在某一时刻并发量上来了,有50个请求需要访问数据库,那么只会有十个请求去成功地访问到数据库中,剩下的40个线程会进入到一个等待状态,只有当十个已经连接数据库的线程的业务处理完毕,释放掉数据库的连接,才会去通知剩余的40个线程,然后从剩余的40个连接中挑一个线程去进行数据库的连接。
六、调用yield()、sleep()、wait()、notify()等方法对锁有何影响?
调用yield()、sleep()之后,当前线程所持有的锁是不会被释放的
调用wait()方法之后会释放当前线程锁持有的锁,被唤醒之后会重新竞争锁。
调用notify()后对锁没有任何影响,但是在调用notify()时,一定要放在一个同步块里面,线程只有在执行完整个同步块后,才会自然而然释放锁,因此notify()一般放在代码同步块的最后一行。