博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
线程池使用不当也会死锁?
阅读量:5060 次
发布时间:2019-06-12

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

线程池是个好东西,最大线程数限制了服务无限制使用宝贵的操作系统线程,最大队列保护内存溢出,完美!

但是线程池使用不当也会导致死锁。这种死锁,要是不知道原理,死都不知道咋死的,并且非常难定位。大家知道,死锁一般都是由于资源征用引起的。而线程池引起的死锁,可能连个synchronize关键字都没有。连同步都没有,资源争用个毛啊。但是对不起,就是死锁了,线程池罢工了。

笔者第一次踩坑就是一次上线发版前有个线程池的脚本,要跑下线上的一千万条数据,每次跑到二三十万时,就跑不动了,看日志没有任何异常,但就是跑不动了。发布日已过六点,运维同学还等着下班。领导和另一位同事焦头烂额的debug。我低血糖,说去楼下便利店买个叉烧。上楼路上,大脑自动把整个脚本运行了一遍,有一个Task内嵌套Task的结构,两边Task使用的是同一个线程池。我也没想清楚,隐隐觉得这里有问题。吞掉整个叉烧,对同事说,把第二个Task改成串行试试。神奇!死锁消失了。

今天重新思考了这个问题,才算明白这次踩坑的缘由。

先说说线程池是怎么会事。代码就不上了,java1.5+和Spring的大差不差,说破天换到C#,也就是那个意思,还是先说个事吧。

话说,在某个小镇,当地只有三个公务员,负责处理镇上所有公民行政诉求,不论是上户口,登记结婚离婚,房产买卖过户。这个镇上的公民素质非常高,去政府办事一定会排队。有一天,张三去给女儿上户口,如下图所示,他经过漫长的排队,终于到了窗口1开始办事,结果公务员1告诉他,他必须提供房产证明。张三一想,这队排了老半天了,也不能重新排啊。就给老婆打电话,让老婆拿着材料来办理。张三老婆来了,保安说,你要办房产证明,得,在最后面排队吧。张三这边,给公务员一说,老婆去办房产证明了,马上就好。公务员也不急,说,那咱等着吧。站三就和公务员1面对面等起来了。后面排队的素质也真高,都不崔。不巧的是,今天李四/王五也都是给孩子上户口,都把自己老婆叫来排队办房产证明。结果当天,这三个公务员到下班也没办成一件事,大家都傻等了一天。

这个故事告诉我们,公务员是靠不住的,不好意思,拿错台词了,这就是线程饥饿死锁的故事。

三个公务员就是三个线程,排队的老百姓就是任务阻塞队列。当张三和公务员1在那傻等时,就是线程1阻塞了。站三叫自己老婆排队来办证明,就是一个Task中嵌套了新的Task,并且等待这个Task的回调模式。当Task嵌套了Task就有可能造成饥饿死锁。

 

好了,故事讲完,线程池饥饿死锁的故事就讲完了。结论很简单,当使用future模式时,不要再Task中嵌套Task,否则线程就死给你看。

死锁的代码什么样?下面贴段代码吧,大概就长这样。

1 package com.shlugood.mapp.service; 2  3 import org.junit.Before; 4 import org.junit.Test; 5 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 6  7 import java.util.ArrayList; 8 import java.util.List; 9 import java.util.concurrent.Callable;10 import java.util.concurrent.ExecutionException;11 import java.util.concurrent.Future;12 13 /**14  *  饥饿死锁测试15  */16 public class HungryDeadLockTest {17 18 19     @Before20     public void threadPoolTaskExecutor() {21         executor = new ThreadPoolTaskExecutor();22         executor.setCorePoolSize(4);23         executor.setMaxPoolSize(4);24         executor.setQueueCapacity(10);25         executor.setThreadNamePrefix("default_task_executor_thread");26         executor.initialize();27     }28 29     private ThreadPoolTaskExecutor executor;30 31     @Test32     public void test() throws ExecutionException, InterruptedException {33         int loop = 0;34         while (true) {35             System.out.println("loop start. loop = " + (loop));36             innerFutureAndOutFuture();37             System.out.println("loop end. loop = " + (loop++));38             Thread.sleep(10);39         }40     }41 42     public void innerFutureAndOutFuture() throws ExecutionException, InterruptedException {43         Callable
innerCallable = new Callable
() {44 @Override45 public String call() throws Exception {46 Thread.sleep(100);47 return "inner callable";48 }49 };50 51 Callable
outerCallable = new Callable
() {52 @Override53 public String call() throws Exception {54 Thread.sleep(10);55 Future
innerFuture = executor.submit(innerCallable);56 String innerResult = innerFuture.get();57 Thread.sleep(10);58 return "outer callable. inner result = " + innerResult;59 }60 };61 62 List
> futures = new ArrayList<>();63 for(int i = 0; i< 10; i++){64 System.out.println("submit : " + i);65 Future
outerFuture = executor.submit(outerCallable);66 futures.add(outerFuture);67 }68 for(int i = 0; i < 10; i++){69 String outerResult = futures.get(i).get();70 System.out.println(outerResult + ":" + i);71 }72 }73 }
View Code

运行后结果,10个任务都提交了,没有任务返回,显示死锁了。

loop start. loop = 0submit : 0submit : 1submit : 2submit : 3submit : 4submit : 5submit : 6submit : 7submit : 8submit : 9

 

转载于:https://www.cnblogs.com/caoshenglu/p/9461567.html

你可能感兴趣的文章
同事间一些搞笑的事情
查看>>
Python 基础篇(2)
查看>>
NOIP临考经验【转】
查看>>
ZOJ 3235 Prototype
查看>>
Best Practices in Asynchronous Programming
查看>>
数组的使用方法
查看>>
64位操作系统下IIS报“试图加载格式不正确的程序”错误
查看>>
存储过程里构建模糊查询,引号的处理
查看>>
黑莓游戏引擎
查看>>
Dijkstra算法和其邻接矩阵实现
查看>>
IE6 中 window.location.href 不能跳转
查看>>
v形 加强版
查看>>
EMVTag系列12《卡片内部风险管理数据》
查看>>
Recommendation system
查看>>
linux批量删除进程
查看>>
第二次代码作业
查看>>
[转载] 自定义标签,jsp调用java类
查看>>
2017-12 CDQZ集训(已完结)
查看>>
10.0.4_CentOS_120g_for_Qt5.3.2
查看>>
题画【沙枣树】
查看>>