博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【并发编程】concurrent包的Condition使用详解
阅读量:4079 次
发布时间:2019-05-25

本文共 4469 字,大约阅读时间需要 14 分钟。

最近使用Condition时有一些疑惑,于是自己做了几个实验,了解了Condition的具体用法,现记录如下。

       首先我们直接来看一段之前网上看到的一个输出1-9的例子,当然网上那个例子是有缺陷的,并不能保证每次都能输出1-9,后面会介绍原因,先看如下代码:

public class ConditionTest {
public static void main(String[] args) { final AtomicInteger value = new AtomicInteger(1); final Lock lock = new ReentrantLock(); final Condition reachThreeCondition = lock.newCondition(); final Condition reachSixCondition = lock.newCondition(); Thread threadA = new Thread(new Runnable() { public void run() { lock.lock(); System.out.println("线程1拿到锁【1】"); try { // 输出1-3 之后激活reachThreeCondition while (value.get() <= 3) { System.out.println(value.get()); value.addAndGet(1); } reachThreeCondition.signal(); System.out.println("线程1【1】调用唤醒【reachThreeCondition】"); } finally { lock.unlock(); System.out.println("线程1【1】释放锁"); } lock.lock(); System.out.println("线程1拿到锁【2】"); try { // 挂起等待唤醒reachSixCondition操作则继续输出后续数字 System.out.println("线程1【2】进入等待"); reachSixCondition.await(); System.out.println("线程1【2】被唤醒"); // 输出剩余数字 while (value.get() <= 9) { System.out.println(value.get()); value.addAndGet(1); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }); Thread threadB = new Thread(new Runnable() { public void run() { try { lock.lock(); System.out.println("线程2拿到锁【1】"); while (value.get() <= 3) { // 线程挂起等待输出1-3之后唤醒 System.out.println("线程2【1】进入等待"); reachThreeCondition.await(); System.out.println("线程2【1】被唤醒"); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); System.out.println("线程2【1】释放了锁"); } try { lock.lock(); System.out.println("线程2拿到锁【2】"); while (value.get() <= 6) { System.out.println(value.get()); value.addAndGet(1); } // 唤醒reachSixCondition继续输出后续数字 reachSixCondition.signal(); System.out.println("线程2【2】唤醒了【reachSixCondition】"); } finally { lock.unlock(); System.out.println("线程2【2】释放了锁"); } } }); // 启动两个线程 threadB.start(); threadA.start(); }}

输出结果1:

线程2拿到锁【1】线程2【1】进入等待线程1拿到锁【1】123线程1【1】调用唤醒【reachThreeCondition】线程1【1】释放锁线程1拿到锁【2】线程1【2】进入等待线程2【1】被唤醒线程2【1】释放了锁线程2拿到锁【2】456线程2【2】唤醒了【reachSixCondition】线程2【2】释放了锁线程1【2】被唤醒789

输出结果2:

线程2拿到锁【1】线程2【1】进入等待线程1拿到锁【1】123线程1【1】调用唤醒【reachThreeCondition】线程1【1】释放锁线程2【1】被唤醒线程2【1】释放了锁线程2拿到锁【2】456线程2【2】唤醒了【reachSixCondition】线程2【2】释放了锁线程1拿到锁【2】线程1【2】进入等待

输出结果3:

线程1拿到锁【1】123线程1【1】调用唤醒【reachThreeCondition】线程1【1】释放锁线程1拿到锁【2】线程1【2】进入等待线程2拿到锁【1】线程2【1】释放了锁线程2拿到锁【2】456线程2【2】唤醒了【reachSixCondition】线程2【2】释放了锁线程1【2】被唤醒789

       上面列出了三种输出结果,结果1其实是一个很理想的运行结果,所有代码都执行完了,其实读者可以反复运行上面代码,会发现可能出现好几种结果,这里只列出三种典型的结果。

       我们来根据结果做如下分析:
1. 线程1和线程2同时启动,此时首次出现锁竞争,上述结果1是线程2获取到锁(此处也可能是线程1获取到锁,见结果3),则线程1处于挂起状态,等待获取锁,线程2获取锁后判断满足条件,则使用reachThreeCondition.await()让线程进入挂起状态,且此处会释放锁
2. 由于上一步线程2释放了锁,所以此时线程1可获取到锁,于是线程1获取到锁,并继续执行输出123,之后调用reachThreeCondition.signal()唤醒线程2,此时线程1和线程2同时运行,线程1和2分别继续执行后续释放锁的代码(此时线程2其实没有锁可释放)。
3. 此时又会出现锁竞争,线程1和线程2会竞争锁(结果1和结果3是由线程1拿到了锁,结果2是由线程2拿到了锁),此时分两种情况:
a.(1)线程1拿到锁,之后满足条件通过reachSixCondition挂起并释放锁,(2)此时线程2拿到锁输出456之后唤醒reachSixCondition,之后俩线程继续各自执行后续代码到程序结束,完整输出1-9.
b.线程2拿到锁,之后不满足条件释放锁,此时第三次出现锁竞争,会出现两种结果,1)线程1拿到锁则执行a逻辑,完整输出1-9;2)线程2拿到锁,则执行线程2输出4-6,之后释放锁,线程1获取锁,之后执行reachSixCondition挂起,由于此时线程2已执行结束,则线程1的reachSixCondition没有地方被唤醒,所以线程1会一直处于挂起状态。

所以通过上面跟踪代码,理解了Condition的使用,其实在于可以根据不同条件来挂起、唤醒线程,且会自动释放锁,以达到可以更加方便的实现线程的有序性


欢迎关注个人博客:blog.scarlettbai.com

转载地址:http://lesni.baihongyu.com/

你可能感兴趣的文章
原来k8s docker是用go语言写的,和现在所讲的go是一个东西!
查看>>
STM32CubeMX 真的不要太好用
查看>>
STM32CubeMX介绍、下载与安装
查看>>
电机和桨叶要搭配选择
查看>>
现在发现如果无人机的电机不同,浆可能是不能混用的。
查看>>
不要买铝合金机架的无人机,不耐摔,易变形弯曲。
查看>>
ACfly也是基于FreeRTOS的
查看>>
F330装GPS的位置
查看>>
STM32时钟系统
查看>>
我想先用三个或者五个激光测距做无人机的室内定位和避障
查看>>
pixhawk也可以用Airsim仿真
查看>>
《无人机电机与电调技术》可以看看
查看>>
我发现七月在线的GAAS课程基本都讲到了
查看>>
电机堵转
查看>>
一个真正好的无人机应该是需要自己慢慢去调参的,别人的默认参数是可以飞但是可能达不到perfect的效果。
查看>>
carzepony也在想往FreeRTOS上迁移
查看>>
可以买个好点的电烙铁
查看>>
ACfly调参记录(包括ACfly-F330和ACfly-T265)
查看>>
一定记得每飞几次或者隔一天要把螺丝和浆帽拧一次,确实会松的
查看>>
《多旋翼无人飞行器嵌入式飞控开发指南》里基于FreeRTOS的无人机软件框架
查看>>