一提到死锁,很多新手可能想到就是两个或两个以上的线程互相持有对方所需要的资源就会,由于synchronized的特性,一个线程持有一个资源,或者说获得一个锁,在该线程释放这个锁之前,其它线程是获取不到这个锁的,而且会一直死等下去,因此这便造成了死锁。
但是在使用线程池的时候不规范的使用也会造成死锁:多个任务通用一个线程池,而且每个任务内也用到该线程池,线程池的数量等于或小于任务数,也会造成死锁
案例:
下面两个类为线程池的配置,注意我这里核心线程数设为10。
@Configuration
@EnableAsync
public class ExecutorConfig {
@Bean("threadPool")
public ThreadPoolTaskExecutor asyncServiceExecutor() {
ThreadPoolTaskExecutor executor = new MyThreadPoolExecutor();
//配置核心线程数-示例大小,按需配置
executor.setCorePoolSize(10);
//配置最大线程数-示例大小,按需配置
executor.setMaxPoolSize(10);
//配置空闲线程存活时间
executor.setKeepAliveSeconds(60);
//配置队列大小-示例大小,按需配置
executor.setQueueCapacity(100);
//配置线程池中的线程的名称前缀
executor.setThreadNamePrefix("demo-Thread");
// 配置拒绝策略:当pool已经达到max size的时候,如何处理新任务
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}
}
/**
* @Author liaojiexin
* @Description 多线程 https://www.cnblogs.com/dolphin0520/p/3932921.html
* https://blog.csdn.net/aotumemedazhao1996/article/details/106322291
* @Date 2021/1/8 16:13
* @Param
* @return
**/
@Slf4j
public class MyThreadPoolExecutor extends ThreadPoolTaskExecutor{
private void showThreadPoolInfo(String prefix){
ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();
if(null==threadPoolExecutor){
return;
}
log.info("{}, {},未完成任务数量 [{}], 完成任务数 [{}], 线程池中存活的线程数量 [{}], 队列大小 [{}]",
this.getThreadNamePrefix(),
prefix,
threadPoolExecutor.getTaskCount(),
threadPoolExecutor.getCompletedTaskCount(),
threadPoolExecutor.getActiveCount(),
threadPoolExecutor.getQueue().size());
}
@Override
public void execute(Runnable task) {
showThreadPoolInfo("执行Runnable任务");
super.execute(task);
}
@Override
public void execute(Runnable task, long startTimeout) {
showThreadPoolInfo("执行Runnable任务");
super.execute(task, startTimeout);
}
@Override
public Future<?> submit(Runnable task) {
showThreadPoolInfo("submit task");
return super.submit(task);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
showThreadPoolInfo("submit callable");
return super.submit(task);
}
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
showThreadPoolInfo("submitListenable");
return super.submitListenable(task);
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
showThreadPoolInfo("submitListenable");
return super.submitListenable(task);
}
}
下面的portFlow方法中for循环内使用了线程池去调用Flow方法,而Flow中也用到了同一个线程池。注意上面核心线程数已经设定为10了,如果portFlow循环了10次,你就会发现发生了死锁,死锁的位置在Flow中的HistoryGetResponse response1=future1.get();这一段代码上。
这是因为核心线程数为10,portFlow中循环了10次,也就是已经把核心线程数用完了(这里要注意线程分配的顺序,先在portFlow中for循环先把所有线程数分配完,再到Flow中进行分配,具体可以自己通过debug来实践一下),这个时候在Flow方法内因为没有核心线程数可用了,所以就一直处于等待状态,而portFlow方法中一直在等待Flow方法返回,所以也一直在等待,这样就会造成死锁。
解决方法就是多个任务内有使用到线程池的时候,这些任务尽可能不要使用同一个线程池,或者核心线程数设定好
//线程池使用
@Service
public class PortMonitorServiceImpl extends ZabbixApiBase implements PortMonitorService {
@Autowired
private PortMonitorDao portMonitorDao;
@Autowired
private PortDao portDao;
@Autowired
private ItemService itemService;
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
/**
* @Author liaojiexin
* @Description
* @Date 2021/1/18 11:26
* @Param [portMonitor, startDate, endDate]
* @return java.util.Map<java.lang.String,java.lang.Object>
**/
@Override
public Map<String, Object> portFlow(PortMonitor portMonitor, Long startDate, Long endDate) throws ExecutionException, InterruptedException, ZabbixApiException {
//此处代码省略
List<Port> list = portDao.selectPortMonitor(port1);
List<Future<String>> futures = new ArrayList<>();
for (Port port : list) {
Long finalStartDate = startDate;
Long finalEndDate = endDate;
Future<String> future = taskExecutor.submit(() -> {
String threadName = Thread.currentThread().getName();
Map<String, Object> map1 = Flow(port.getIfIndex(), finalStartDate, finalEndDate);
map.put(port.getPortName(), map1);
return threadName;
});
futures.add(future);
}
//如果没有这个循环,主线程和主线程就是独立的 https://www.cnblogs.com/zhangzonghua/p/12878245.html
for(Future<String> future : futures) {
System.out.println("主线程输出:" + future.get());
}
System.out.println("结束:" + new Date().getTime());
return map;
}
@Override
public Map<String, Object> Flow(Integer key, Long startDate, Long endDate) throws ZabbixApiException, ExecutionException, InterruptedException {
Map<String, Object> map = new ConcurrentSkipListMap<>();
//每秒接收字节速率
String key1 = ("\"net.if.rx.bps[" + key + "]\"").trim();
Integer itemId1 = itemService.SelectItemId(key1); //监控项id
List<Integer> itemidsList1 = new ArrayList<>();
itemidsList1.add(itemId1);
//请求
HistoryGetRequest request1 = new HistoryGetRequest();
//参数
HistoryGetRequest.Params params1 = request1.getParams();
params1.setTime_from(startDate); //开始时间
params1.setTime_till(endDate); //结束时间
params1.setItemids(itemidsList1); //监控项id
params1.setHistory(0);
params1.setSortField("clock");
params1.setSortorder("ASC");
//发送请求
Future<HistoryGetResponse> future1 = taskExecutor.submit(() -> {
HistoryGetResponse response1 = zabbixApi.history().get(request1);
return response1;
});
//数据处理
SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm:ss");
HistoryGetResponse response1=future1.get();
for (int i = 0; i < response1.getResult().size(); i++) {
//此处代码省略
}
return map;
}
}