难度
初级
学习时间
30分钟
适合人群
零基础
开发语言
Java
开发环境
- JDK v11
- IntelliJ IDEA v2018.3
友情提示
- 本教学属于系列教学,内容具有连贯性,本章使用到的内容之前教学中都有详细讲解。
- 本章内容针对零基础或基础较差的同学比较友好,可能对于有基础的同学来说很简单,希望大家可以根据自己的实际情况选择继续看完或等待看下一篇文章。谢谢大家的谅解!
1.温故知新
前面在 一章中介绍了 synchronized关键字 。
在 一章中介绍了 同步代码块/方法中的同步锁 。
在 一章中介绍了 同步代码块中的双重判断 。
在 一章中介绍了 死锁 。
在 一章中介绍了 同步方法产生死锁的例子 。
在 一章中介绍了 同步代码块产生死锁的例子 。
在 一章中介绍了 饥饿线程(Starvation) 。
现在我们来讲解 活锁(Livelock) 。
2.什么是活锁?
难道活锁就是死锁以外的情况?
非也。
死锁是这样的:
活锁是这样的:
3.活锁的定义
下面我们对定义通过分词解释+举例说明。
第一句, 一个线程通常用于响应另一个线程的操作 :
该句说是线程间的协作。
什么称之为协作呢?
比如,一个人负责正在打包货物,打包好了之后,另一个人负责派送:
这就是协作。
那一个线程响应另一个线程的操作体现在哪呢?
例如上图中的线程B就是用于响应线程A的操作。
第二句,如果另一个线程的操作也是对另一个线程的操作的响应,则可能导致活锁:
该句的意思就是几个线程间有可能相互阻挡,从而发生死锁。
例如,两辆相向驶来的车:
左边的车给右边的车让行,右边的车给左边的车让行,它们会永远相互阻挡。
第三句,与死锁一样,活锁线程无法取得进一步进展:
这句很好理解,两个线程永远相互阻挡和死锁一样,都无法继续向前执行。
例如,这两辆车永远这样阻挡下去,谁都无法通行。
4.活锁程序
单线程是不可能发生活锁情况的,所以活锁产生的情况至少需要两个或两个以上的线程。
这里,我们来两个线程:
我们这里就把上面那两个汽车的例子用程序描述一下。
接着,再来一个汽车类Car:
在汽车类里面我们还需要一个汽车行驶的方向,用一个String类型的属性来表示:
然后,为direction属性提供getter、setter方法:
接着,加入颜色属性以示区分汽车之间的不同:
因为汽车一创建的时候就需要知道汽车的颜色,所以我们在Car类的构造方法上添加一个类型为汽车颜色的参数:
然后,写一个汽车行驶的方法:
汽车类写好了。
接着,我们在Main类中去创建两辆汽车:
从图中可以看到一辆汽车为redCar(红车),另一辆汽车为blueCar(蓝车)。
然后,设置它们行驶在上方车道还是下方车道:
现在要将汽车对象和线程关联起来,我们使用匿名内部类方式实现Runnable接口:
run()方法里面怎么写呢?
根据上面小节动画演示来看,我们需要判断对方车辆是否跟我方车辆在同一车道上,如果是在同一车道上,则我方汽车行驶方向调整至另外一条车道上,对方汽车也是如此。
思路有了,于是开始书写run()方法内部实现:
接着,我们启动线程:
最后,我们把代码整理一下,再来运行程序。
演示:
请写一个活锁例子。
请观察程序代码及结果。
代码:
Car类:
Main类:
结果:
从运行结果来看,显然不符合预期。
问题出在我们判断我方汽车与对方汽车是否在同一车道后,没有继续调用汽车的行驶方法,导致看不到新的汽车行驶状态。
问题是这样写了之后还是有问题,如果下次还同一车道呢?再把判断的代码和汽车行驶的代码复制一遍吗?
我们肯定不能这么干,if语句判断一次, while 语句判断多次,所以我们把if语句改为while语句即可:
改完之后,我们再运行程序,看看执行结果:
从运行结果来看,好像没有出现活锁,其实不然, 如果两个线程到达同时执行的话,活锁一定会出现。 这里我们的线程一前一后,活锁很难出现。
当然了,有小伙伴依然很想看到活锁的出现,那么再给大家一个非常简易的例子,这个例子不具备很强的说服力。
演示:
请写一个活锁简易例子。
请观察程序代码及结果。
代码:
Main类:
结果:
从运行结果来看,活锁成功产生。
简单的来分析一下这个例子,首先,我们在run()方法里面只写了一个while循环:
while循环判断的条件大家注意一下:
很奇怪,这个条件就是一个 结果永远为false的表达式 。当然了,这里把判断条件写得太无脑,但初衷是希望大家可以通过这个简单的例子来看看活锁是怎么一回事,再加以理解。
大家想想,我们汽车的例子:
这两辆车是不是永远都在重复做着一件事:让行,而且这件事还是失败的。
为什么说是失败的呢?
因为它们谁也没通过,错误的事还不停的做,所以说是失败的。
线程不断地在重复执行错误的任务,不断地在重试。一件失败的事不断的重试这也是活锁 。
最后,希望大家可以再结合动画好好体会体会,特别是最后一个例子。
GitHub
本章程序GitHub地址:
总结
- 一个线程通常用于响应另一个线程的操作。如果另一个线程的操作也是对另一个线程的操作的响应,则可能导致活锁。与死锁一样,活锁线程无法取得进一步进展。
至此,Java中活锁(Livelock)相关内容讲解先告一段落,更多内容请持续关注。
答疑
如果大家有问题或想了解更多前沿技术,请在下方留言或评论,我会为大家解答。
上一章
下一章
“全栈2019”Java多线程第二十四章:等待唤醒机制详解
学习小组
加入同步学习小组,共同交流与进步。
- 方式一:关注头条号Gorhaf,私信“Java学习小组”。
- 方式二:关注公众号Gorhaf,回复“Java学习小组”。
全栈工程师学习计划
关注我们,加入“全栈工程师学习计划”。
版权声明
原创不易,未经允许不得转载!