当前位置:网站首页>【面试:并发篇21:多线程:活跃性】死锁、活锁、饥饿
【面试:并发篇21:多线程:活跃性】死锁、活锁、饥饿
2022-07-23 00:51:00 【I cream】
【面试:并发篇21:多线程:活跃性】死锁、活锁、饥饿
00.前言
如果有任何问题请指出,感谢。
01.介绍
死锁:两个线程 t1 t2,两把锁 A B,t1持有A锁 t2持有B锁,此时t1想要再获得B锁 t2想要再获得A锁,但是它们都不释放自己所持有的锁,最终导致死锁。
活锁:两个线程互相改变对方的结束条件,导致最后谁也没办法结束
饥饿:多个线程中 有一个线程t,由于线程之间的冲突 导致t线程分的时间片低,导致t线程基本不运行,这种情况下t线程就处于饥饿
02.死锁
案例
两个线程 t1 t2,两把锁 A B,t1持有A锁 t2持有B锁,此时t1想要再获得B锁 t2想要再获得A锁
package cn.itcast.n4.deadlock;
import lombok.extern.slf4j.Slf4j;
import static cn.itcast.n2.util.Sleeper.sleep;
@Slf4j(topic = "c.TestDeadLock")
public class TestDeadLock {
public static void main(String[] args) {
test1();
}
private static void test1() {
Object A = new Object();
Object B = new Object();
Thread t1 = new Thread(() -> {
synchronized (A) {
log.debug("lock A");
sleep(1);
synchronized (B) {
log.debug("lock B");
log.debug("操作...");
}
}
}, "t1");
Thread t2 = new Thread(() -> {
synchronized (B) {
log.debug("lock B");
sleep(0.5);
synchronized (A) {
log.debug("lock A");
log.debug("操作...");
}
}
}, "t2");
t1.start();
t2.start();
}
}
结果
01:08:32.822 c.TestDeadLock [t1] - lock A
01:08:32.822 c.TestDeadLock [t2] - lock B
解释
可以看出t1 t2线程都尝试获取对方的锁,但最终陷入死锁,代码没有往下执行
定位死锁
前提:运行上述案例的代码,用下面的方法查看是否死锁 以及 死锁信息
方法一:命令行jstack
首先我们用jps命令找到 我们需要的java程序 也就是 TestDeadLock
然后我们用jstack命令 查看19008进程的线程详细信息
我们在里面发现了记录死锁的信息,可以看出是 t1 t2线程死锁
再后面列出死锁的详细信息,包括死锁的位置
方法二
通过图像界面工具jconsole来查看
首先我们win+R搜索jconsole
然后进入图形界面选择TestDeadLock 然后连接
然后我们点击左上方线程,查看线程的详细信息,再点击左下角的检查死锁
我们看出死锁线程为 t1 t2,点击t1 t2线程可以查看详细信息
03.死锁-哲学家就餐问题
介绍

哲学家就餐问题是操作系统中介绍死锁时的经典案例。
这个故事说的是,有五位哲学家 坐在圆桌旁,他们只做两件事,吃饭和思考,思考一会吃口饭 吃完饭继续思考。吃饭时需要两根筷子,桌子上共有5根筷子,每位哲学家左右手各有一根筷子,如果身边的筷子被拿完,自己就得等待。
条件补充
我们规定 每个人都先拿自己左手边的筷子 再拿右手边的筷子。
问题矛盾点
我们发现 如果按上述描述规则吃饭,势必会有一种情况 就是 每个人都拿到自己左手边的筷子 但还需要右手边的筷子,但是右手边的筷子已经被占用,导致死锁。
代码
public class TestDeadLock {
public static void main(String[] args) {
Chopstick c1 = new Chopstick("1");
Chopstick c2 = new Chopstick("2");
Chopstick c3 = new Chopstick("3");
Chopstick c4 = new Chopstick("4");
Chopstick c5 = new Chopstick("5");
new Philosopher("苏格拉底", c1, c2).start();
new Philosopher("柏拉图", c2, c3).start();
new Philosopher("亚里士多德", c3, c4).start();
new Philosopher("赫拉克利特", c4, c5).start();
new Philosopher("阿基米德", c5, c1).start();
}
}
@Slf4j(topic = "c.Philosopher")
class Philosopher extends Thread {
Chopstick left;
Chopstick right;
public Philosopher(String name, Chopstick left, Chopstick right) {
super(name);
this.left = left;
this.right = right;
}
@Override
public void run() {
while (true) {
// 尝试获得左手筷子
synchronized (left) {
// 尝试获得右手筷子
synchronized (right) {
eat();
}
}
}
}
Random random = new Random();
private void eat() {
log.debug("eating...");
Sleeper.sleep(0.5);
}
}
class Chopstick {
String name;
public Chopstick(String name) {
this.name = name;
}
@Override
public String toString() {
return "筷子{" + name + '}';
}
}
结果
01:48:37.984 c.Philosopher [苏格拉底] - eating…
01:48:37.984 c.Philosopher [亚里士多德] - eating…
01:48:38.493 c.Philosopher [阿基米德] - eating…
01:48:38.493 c.Philosopher [柏拉图] - eating…
01:48:39.003 c.Philosopher [柏拉图] - eating…
01:48:39.003 c.Philosopher [赫拉克利特] - eating…
01:48:39.513 c.Philosopher [苏格拉底] - eating…
01:48:39.513 c.Philosopher [赫拉克利特] - eating…
解释
我们创建一个筷子类Chopstick,一个哲学家类Philosopher,创建了五个筷子类对象 五个哲学家类对象,哲学家每次先尝试获取左边的筷子 再尝试获取右边的筷子,可以看到结果 产生了死锁。
根据jstack的结果 我们可以看出,确实发生了死锁,原因是五个哲学家都需要自己右手边哲学家的筷子
04.活锁
介绍
两个线程互相改变对方的结束条件,导致最后谁也没办法结束
例子
两个线程t1 t2 一个成员变量count=10,t1线程的结束条件是count<=0,t2线程的结束条件是count>=20,但是t1线程是在不断减少count,t2线程是在不断增加count,最终导致两个线程动达不到结束条件 导致活锁
代码
@Slf4j(topic = "c.TestLiveLock")
public class TestLiveLock {
static volatile int count = 10;
static final Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
// 期望减到 0 退出循环
while (count > 0) {
sleep(0.2);
count--;
log.debug("count: {}", count);
}
}, "t1").start();
new Thread(() -> {
// 期望超过 20 退出循环
while (count < 20) {
sleep(0.2);
count++;
log.debug("count: {}", count);
}
}, "t2").start();
}
}
结果
一直运行,数值保持在0~20期间
解决方法
把两个线程指令执行的时间进行交错就行了,具体就是暂停时间改为随机
05.饥饿
介绍
多个线程中 有一个线程t,由于线程之间的冲突 导致t线程分的时间片低,导致t线程基本不运行,这种情况下t线程就处于饥饿
案例
我们前面遇见了死锁-哲学家问题,这个问题可以通过 顺序加锁的方法解决,但是这个方法会导致饥饿
代码
public class TestDeadLock {
public static void main(String[] args) {
Chopstick c1 = new Chopstick("1");
Chopstick c2 = new Chopstick("2");
Chopstick c3 = new Chopstick("3");
Chopstick c4 = new Chopstick("4");
Chopstick c5 = new Chopstick("5");
new Philosopher("苏格拉底", c1, c2).start();
new Philosopher("柏拉图", c2, c3).start();
new Philosopher("亚里士多德", c3, c4).start();
new Philosopher("赫拉克利特", c4, c5).start();
new Philosopher("阿基米德", c1, c5).start();
}
}
@Slf4j(topic = "c.Philosopher")
class Philosopher extends Thread {
Chopstick left;
Chopstick right;
public Philosopher(String name, Chopstick left, Chopstick right) {
super(name);
this.left = left;
this.right = right;
}
@Override
public void run() {
while (true) {
// 尝试获得左手筷子
synchronized (left) {
// 尝试获得右手筷子
synchronized (right) {
eat();
}
}
}
}
Random random = new Random();
private void eat() {
log.debug("eating...");
Sleeper.sleep(0.5);
}
}
class Chopstick {
String name;
public Chopstick(String name) {
this.name = name;
}
@Override
public String toString() {
return "筷子{" + name + '}';
}
}
结果
02:32:24.639 c.Philosopher [阿基米德] - eating…
02:32:24.639 c.Philosopher [亚里士多德] - eating…
02:32:25.149 c.Philosopher [赫拉克利特] - eating…
02:32:25.658 c.Philosopher [赫拉克利特] - eating…
02:32:26.168 c.Philosopher [亚里士多德] - eating…
02:32:26.168 c.Philosopher [阿基米德] - eating…
02:32:26.678 c.Philosopher [亚里士多德] - eating…
02:32:26.678 c.Philosopher [阿基米德] - eating…
02:32:27.188 c.Philosopher [赫拉克利特] - eating…
02:32:27.697 c.Philosopher [阿基米德] - eating…
02:32:27.697 c.Philosopher [亚里士多德] - eating…
02:32:28.207 c.Philosopher [赫拉克利特] - eating…
02:32:28.717 c.Philosopher [赫拉克利特] - eating…
02:32:29.226 c.Philosopher [赫拉克利特] - eating…
02:32:29.736 c.Philosopher [阿基米德] - eating…
02:32:29.736 c.Philosopher [亚里士多德] - eating…
02:32:30.246 c.Philosopher [阿基米德] - eating…
02:32:30.246 c.Philosopher [亚里士多德] - eating…
02:32:30.756 c.Philosopher [赫拉克利特] - eating…
02:32:31.265 c.Philosopher [赫拉克利特] - eating…
02:32:31.775 c.Philosopher [亚里士多德] - eating…
02:32:31.775 c.Philosopher [阿基米德] - eating…
02:32:32.285 c.Philosopher [亚里士多德] - eating…
02:32:32.795 c.Philosopher [亚里士多德] - eating…
02:32:33.304 c.Philosopher [赫拉克利特] - eating…
02:32:33.814 c.Philosopher [亚里士多德] - eating…
02:32:34.324 c.Philosopher [赫拉克利特] - eating…
02:32:34.833 c.Philosopher [赫拉克利特] - eating…
02:32:35.343 c.Philosopher [赫拉克利特] - eating…
02:32:35.853 c.Philosopher [赫拉克利特] - eating…
02:32:36.363 c.Philosopher [赫拉克利特] - eating…
02:32:36.872 c.Philosopher [赫拉克利特] - eating…
解释
观察下面两个图

我们通过顺序加锁 解决了死锁问题,但也同时使得 阿基米德竞争加大获得两个筷子的概率大大下降 使得赫拉克利特竞争减小获得两个筷子的概率大大增强,也就是最终结果显示的那样。
阿基米德现在的这种情况就被成为饥饿。
如何解决饥饿
后续文章会讲解ReentrantLock 它可以解决。
边栏推荐
- XSS labs customs collection
- Anti attack based on conjugate gradient method
- NodeJS 基于 Dapr 构建云原生微服务应用,从 0 到 1 快速上手指南
- 【C语言】文件操作
- [try to hack] awvs installation and simple use
- 50道经典计算机网络面试题,你答得上几个?(三)
- 在线抠图和换背景及擦除工具
- Practical exercise | a simple method for MySQL processlist table and Navicat monitor to identify slow queries
- DALSA智能相机BOA Spot与西门子S7-1200 Profinet通讯
- 【ManageEngine】网络配置管理的6大必备功能
猜你喜欢

Anti attack based on conjugate gradient method
![[ManageEngine] six essential functions of network configuration management](/img/dc/df353da0e93e4d936c39a39493b508.png)
[ManageEngine] six essential functions of network configuration management

Summary of some open source libraries that drive MCU hardware debugger (including stlink debugger)

50道经典计算机网络面试题,你答得上几个?(二)

.NET开发云原生应用,你只差给自己加个油

模板学堂丨JumpServer安全运维审计大屏

DOM series prohibit selected text and prohibit right-click menu

Found a useful data analysis tool

SQL Server database design -- select statement

go语言中的结构体和组合思想入门示例
随机推荐
JMeter --- JMeter installation tutorial
What are the seven layers of OSI's seven layer model? What is the role of each layer? This article is clear!
启牛开户安全性高吗?说万3的佣金靠谱吗?
DALSA智能相机BOA Spot与西门子S7-1200 Profinet通讯
2022.7.22-----leetcode.757
7. Image data processing of paddlepaddle
makefile中include的作用
基于JSP实现OA办公系统
Unity中实现判断Missing还是Null
【ManageEngine】网络配置管理的6大必备功能
UGUI源码解析——Mask
canal实现Mysql数据同步
Mathematical modeling interpolation fitting
Trigger event when input is completed
SPSS Chi-Square
基于SSM的博客系统【带后台管理】
万物互联时代,看IoT测试如何应对“芯”挑战
1646. 获取生成数组中的最大值递归法
解析steam与创客教育课堂的统筹规划
【微信小程序】开发入门篇(二)