记一次线程池死锁

一提到死锁,很多新手可能想到就是两个或两个以上的线程互相持有对方所需要的资源就会,由于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;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
书籍目录: 第1篇 Visual C#基础编程实例 实例1 C#经典程序--Hello World 实例2 C#的简单输入输出(I/O) 实例3 C#的复杂输入输出(I/O) 实例4 通信录管理与维护 实例5 文件与目录管理 实例6 用户界面上的“Hello,World!” 实例7 组件化“Welcome”程序 实例8 网络上的“Hello World” 实例9 Ref、Out与Params描述符的应用 实例10 C#自动内存管理的应用 实例11 一个有趣栈类的实现 实例12 垃圾收集器管理与应用 实例13 垃圾收集器算法控制与使用 实例14 调用栈录异常点 实例15 使用C#异常的栈跟踪 实例16 运行期间检测变量类型 实例17 常用值类型的原型定义 实例18 打印杨辉三角形 实例19 比较学生信息 实例20 获取车辆信息 实例21 简单角色类游戏的制作 实例22 旅馆住宿登情况表制作 实例23 长命名空间的应用 实例24 文件特征计数 实例25 文本框输入数据的验证 第2篇 Visual C#中级编程实例 实例26 窗体背景颜色动态变化 实例27 C#属性及应用 实例28 C#属性Metadata的管理与应用 实例29 使用Context属性创建CallThreshold Service 实例30 使用应用程序域 实例31 创建C#组件与客户应用程序 实例32 用OpenFileDialog类浏览或打开文件 实例33 在C#程序中获得Win32 API 实例34 从C#中调用COM组件 实例35 在C#程序中修改HTML文件标题 实例36 下载Web页面 实例37 创建多线程应用程序 实例38 多线程的多次加载 实例39 单个线程同步运行 实例40 多线程同步运行 实例41 线程Thread Relative Static跟踪与实现 实例42 线程池(ThreadPool)的应用 实例43 多线程互斥运行 实例44 多线程时钟应用程序 实例45 监视多线程 实例46 防止多线程应用程序死锁 实例47 文件同步操作与应用 实例48 在COM程序设计中使用.NET组件 实例49 文件异步操作与多处理器系统 实例50 获取网络主机IP地址 实例51 C#对话信息框的应用 实例52 在C#中快速调用Windows API 实例53 摄氏温度与华氏温度间相互转换(1) 实例54 摄氏温度与华氏温度间相互转换(2) 实例55 使用FileSystemWatcher组件监视Web服务器 实例56 由颜色名字产生对应颜色 实例57 使用索引指示器分析域名 实例58 C#版本的PingC 实例59 DNS客户程序 实例60 一个有趣的DOS实用程序 实例61 代表元基本应用 实例62 有趣的事件代表元 实例63 随机连续偶数发生事件处理 实例64 有趣的列表框窗体 实例65 数学函数应用 第3篇 Visual C#高级编程实例 实例66 文件夹中的文件列表 实例67 读写文本文件 实例68 读写二进制文件 实例69 显示系统日期与时间(1) 实例70 显示差值的日期与时间(2) 实例71 时钟发生器应用 实例72 在.NET程序设计中使用ATL 实例73 浏览Internet文件 实例74 在C#应用程序中打开浏览器 实例75 显示Internet文件信息 实例76 Puzzle游戏 实例77 MDI窗体菜单设计(1) 实例78 MDI窗体菜单设计(2) 实例79 创建一个C#编辑器 实例80 网络端口扫描器 实例81 深入WinForms-地址簿应用(1) 实例82 深入WinForms--Image Viewer应用程序(2) 实例83 开饭时间提醒器(Meal Reminder) 实例84 服务器端C#实例 实例85 数字时钟设计技术 实例86 自动编译C#程序AutoCompiler 实例87 使用C#与ASP+编写File Uploder 实例88 访问注册表中的硬件信息 实例89 设置“开始”菜单 实例90 在“新建”中添加自己的文件类型 实例91 显示Exchange软件的客户名称 实例92 读取Windows注册表信息 实例93 自定义AboutBox组件 实例94 自定义控件及应用 实例95 Java与C#混合编程的应用 实例96 C#与C++混合编程的应用 实例97 C#与VB混合编程的应用 实例98 C#组件与C#客户程序编程的应用 实例99 C#与XML联合应用XMLHelper 实例100 在C#中部署应用

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值