死锁学习总结:
死锁是怎么造成的?
- 多线程锁定同一资源会造成死锁
- 线程池中的任务使用当前线程池也可能出现死锁
参考连接:
https://blog.csdn.net/qq_35064774/article/details/51793656
情况一: 死锁是两个或多个线程互相等待对方所有用的资源情形:现在有线程1和线程2。线程1执行过程中,先锁定了对象a,然后需要再锁定b才能继续执行代码;而线程2正巧相反,先锁定了b,需要再锁定a才能继续执行代码。这时,两个线程都等着对方解锁,才能继续执行,这时,两个线程就进入等待状态,最终不会有线程执行。这就变成了死锁。
接下来是代码实例:
class DeadLock implements Runnable{
boolean lockFormer;
static Object o1 = new Object();
static Object o2 = new Object();
DeadLock(boolean lockFormer){
this.lockFormer = lockFormer;
}
@Override
public void run() {
if (this.lockFormer){
synchronized(o1){
try{
Thread.sleep(500);
System.out.println("线程1");
}catch (Exception e){
e.printStackTrace();
}
synchronized (o2){
System.out.println("lok");
}
}
}else {
synchronized (o2){
try{
Thread.sleep(500);
System.out.println("线程2");
}catch (Exception e){
e.printStackTrace();
}
synchronized (o1){
System.out.println("lok");
}
}
}
}
}
尽量避免加多个锁,避免死锁。
参考连接:
http://www.importnew.com/30277.html#comment-795471
https://www.cnblogs.com/caoshenglu/p/9461567.html
情况二: 线程池自己引发的死锁
单线程使用不当引发死锁,实现代码:
@Slf4j
class DeadLock2 {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(() -> {
try{
log.info("First");
pool.submit(() -> log.info("second")).get();
log.info("third");
}catch (InterruptedException | ExecutionException e){
log.error("Error",e);
}
});
System.out.println("process is over");
}
}
看起来没有什么问题-所有信息都按预期输出:
process is over
15:46:07.470 [pool-1-thread-1] INFO xiaowang.org.prictice.DeadLock2 - First
15:46:07.474 [pool-1-thread-2] INFO xiaowang.org.prictice.DeadLock2 - second
15:46:07.474 [pool-1-thread-1] INFO xiaowang.org.prictice.DeadLock2 - third
注意我们用 get() 阻塞线程,在显示“Third”之前必须等待内部线程(Runnable)运行完成。这是个大坑!等待内部任务完成意味着需要从线程池额外获取一个线程来执行任务。然而,我们已经使用到了一个线程,所以内部任务在获取到第二个线程前将一直阻塞。当前我们的线程池足够大,运行没问题。让我们稍微改变一下代码,将线程池缩减到只有一个线程,另外关键的一点是我们移除 get() 方法:
@Slf4j
class DeadLock2 {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(() -> {
log.info("First");
pool.submit(() -> log.info("second"));
log.info("third");
});
System.out.println("process is over");
}
}
代码运行正常,只是有点乱:
15:51:06.334 [pool-1-thread-1] INFO xiaowang.org.prictice.DeadLock2 - First
15:51:06.339 [pool-1-thread-2] INFO xiaowang.org.prictice.DeadLock2 - second
15:51:06.339 [pool-1-thread-1] INFO xiaowang.org.prictice.DeadLock2 - third
顺序的改变完全在预料之内,没有涉及线程间的竞态条件(事实上我们只有一个线程)。仔细分析一下发生了什么:我们向线程池提交了一个新任务(打印“Second”的任务),但这次我们不需要等待这个任务完成。因为线程池中唯一的线程被打印“First”和“Third”的任务占用,所以这个外层任务继续执行,并打印“Third”。当这个任务完成时,将单个线程释放回线程池,内部任务最终开始执行,并打印“Second”。那么死锁在哪里?来试试在内部任务里加上 get() 方法:
只有一个线程,
ExecutorService pool = Executors.newFixedThreadPool(1)
@Slf4j
class DeadLock2 {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(1);
pool.submit(() -> {
try{
log.info("First");
pool.submit(() -> log.info("second")).get();
log.info("third");
}catch (InterruptedException | ExecutionException e){
log.error("Error",e);
}
});
System.out.println("process is over");
}
}
死锁出现了!我们来一步一步分析:
- 打印“First”的任务被提交到只有一个线程的线程池
- 任务开始执行并打印“First”
- 我们向线程池提交了一个内部任务,来打印“Second”
- 内部任务进入等待任务队列。没有可用线程因为唯一的线程正在被占用
- 我们阻塞住并等待内部任务执行结果。不幸的是,我们等待内部任务的同时也在占用着唯一的可用线程
- get() 方法无限等待,无法获取线程
- 死锁
推荐阅读
-
vue2.0饿了么学习笔记(15)ratingselect组件的实现
-
vue2.0 饿了么学习笔记(17)seller组件开发
-
[WPF 学习] 14.PlaceHolder的简单实现
-
Android学习笔记(Android Studio) 4-1-2 Activity的生命周期(不可不会的Activity和Fragment)
-
DRP总结—Model1和Model2的区别
-
Oracle数据库下约束的创建与管理(主键约束)学习讲解
-
Keras的回调函数ReduceLROnPlateau优化学习率
-
强化学习Deep Q-Learning系列在gym上SpaceInvaders游戏的应用
-
Android学习笔记(Android Studio) 7-1 SharedPreferences 轻量数据存储(数据存储)
-
学习python类方法与对象方法