跑起一个程序,并不难;难的是,能让程序跑多远!—— 一颗剽悍的种子
JUC并发系列
一、什么是Condition
jdk1.5的时候更新了Condition,可以用来替代原先的 wait、notify 实现线程间协作,因为Condition的 await、signal 方式实现线程协作更安全且高效。
可以看之前深入多线程系列,我们使用管程方式和信号灯方式解决生产者与消费者问题就是用 wait、notify 实现。
二.手敲代码实现Condition实现精准通知唤醒
有一个场景,我们想要的一个输出结果是按照ABC的顺序执行,那么我们就用今天的主角 Condition 实现。
2.1没有使用Condition
在实现Condition精确通知唤醒前,先看没有使用Condition输出结果的代码段。(对比着学习更为直观)
public class demo{
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 5; i++) {
data.testA();
}
},"一颗剽悍的种子A").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
data.testB();
}
},"一颗剽悍的种子B").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
data.testC();
}
},"一颗剽悍的种子C").start();
}
}
class Data{
public void testA(){
System.out.println(Thread.currentThread().getName() + "A");
}
public void testB(){
System.out.println(Thread.currentThread().getName() + "B");
}
public void testC(){
System.out.println(Thread.currentThread().getName() + "C");
}
}
运行结果
可以看到顺序乱了,并不是按照我们想要的执行顺序。
2.2使用Condition实现精准通知唤醒
synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock同样是独占锁,但加锁和解锁的过程需要手动进行,不易操作,但非常灵活。
关键代码
手动锁 ->判断 ->执行 ->通知 ->手动关锁
public void testA(){
lock.lock();
try {
while (num != 1){
conditionA.await(); //等待
}
System.out.println(Thread.currentThread().getName() + "A");
num++;
conditionB.signal(); //通知
}catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
全部代码
public class demo{
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (Integer i = 0; i < 5; i++) {
data.testA();
}
},"一颗剽悍的种子A").start();
new Thread(()->{
for (Integer i = 0; i < 5; i++) {
data.testB();
}
},"一颗剽悍的种子B").start();
new Thread(()->{
for (Integer i = 0; i < 5; i++) {
data.testC();
}
},"一颗剽悍的种子C").start();
}
}
class Data{
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
private Condition conditionC = lock.newCondition();
private Integer num = 1; //1 = A 2 = B 3 = C
public void testA(){
lock.lock();
try {
while (num != 1){
conditionA.await(); //等待
}
System.out.println(Thread.currentThread().getName() + "A");
num++;
conditionB.signal(); //通知
}catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void testB(){
lock.lock();
try {
while (num != 2){
conditionB.await(); //等待
}
System.out.println(Thread.currentThread().getName() + "B");
num++;
conditionC.signal(); //通知
}catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void testC(){
lock.lock();
try {
while (num != 3){
conditionC.await(); //等待
}
System.out.println(Thread.currentThread().getName() + "C");
num = 1;
conditionA.signal(); //通知
}catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
运行结果
三、防止虚假唤醒
看过之前的深入多线程系列或上面的代码的小伙伴,如果留心会发现在判断中我们更多的是使用 while 判断,而不是 if,因为 if 可能出现虚假唤醒。
那何为防止虚假唤醒?
所谓虚假唤醒就是线程虽然可以唤醒,但不会被通知,中断或超时。
四、最后
最后的最后,为了更好的阅读体验,我把想说的话都放在了下面,嘿嘿。
我是一颗剽悍的种子 把我会的,认真的分享 是我写博客一直不变的信条。
如果你能看到这篇博文,说明咱们还是很有缘的;希望能带给你一些许帮助,创作的不易, 把我文章的知识带走,你的三连留下,点赞,评论,关注,是我最大的动力。