设为首页 加入收藏

TOP

使用Thread Pool不当引发的死锁(三)
2018-10-23 18:08:34 】 浏览:427
Tags:使用 Thread Pool 不当 引发
hread-1]: Third INFO [pool-1-thread-1]: Second

两点需要注意:

  • 所有代码运行在单个线程上(毫无疑问)
  • “Third”信息显示在“Second”之前

顺序的改变完全在预料之内,没有涉及线程间的竞态条件(事实上我们只有一个线程)。仔细分析一下发生了什么:我们向线程池提交了一个新任务(打印“Second”的任务),但这次我们不需要等待这个任务完成。因为线程池中唯一的线程被打印“First”和“Third”的任务占用,所以这个外层任务继续执行,并打印“Third”。当这个任务完成时,将单个线程释放回线程池,内部任务最终开始执行,并打印“Second”。那么死锁在哪里?来试试在内部任务里加上 get() 方法:

ExecutorService pool = Executors.newSingleThreadExecutor();
pool.submit(() -> {
    try {
        log.info("First");
        pool.submit(() -> log.info("Second")).get();
        log.info("Third");
    } catch (InterruptedException | ExecutionException e) {
        log.error("Error", e);
    }
});

死锁出现了!我们来一步一步分析:

  • 打印“First”的任务被提交到只有一个线程的线程池
  • 任务开始执行并打印“First”
  • 我们向线程池提交了一个内部任务,来打印“Second”
  • 内部任务进入等待任务队列。没有可用线程因为唯一的线程正在被占用
  • 我们阻塞住并等待内部任务执行结果。不幸的是,我们等待内部任务的同时也在占用着唯一的可用线程
  • get() 方法无限等待,无法获取线程
  • 死锁

这是否意味单线程的线程池是不好的?并不是,相同的问题会在任意大小的线程池中出现,只不过是在高负载情况下才会出现,这维护起来更加困难。你在技术层面上可以使用一个无界线程池,但这样太糟糕了。

Reactor/RxJava

请注意,这类问题也会出现在上层库,比如 Reactor

Scheduler pool = Schedulers.fromExecutor(Executors.newFixedThreadPool(10));
Mono
    .fromRunnable(() -> {
        log.info("First");
        Mono
                .fromRunnable(() -> log.info("Second"))
                .subscribeOn(pool)
                .block();  //VERY, VERY BAD!
        log.info("Third");
    })
    .subscribeOn(pool);

当你部署代码,它似乎可以正常工作,但很不符合编程习惯。根源的问题是相通的,最后一行的 subscribeOn() 表示外层任务(Runnable)请求了线程池(pool)中一个线程,同时,内部任务(Runnable)也试图获取一个线程。如果把基础的线程池换成只包含单个线程的线程池,会发生死锁。对于 RxJava/Reactor 来说,解决方案很简单——用异步操作替代阻塞操作。

Mono
    .fromRunnable(() -> {
        log.info("First");
        log.info("Third");
    })
    .then(Mono
            .fromRunnable(() -> log.info("Second"))
            .subscribeOn(pool))
    .subscribeOn(pool)

防患于未然

并没有彻底避免死锁的方法。试图解决问题的技术手段往往会带来死锁风险,比如共享资源和排它锁。如果无法根治死锁(或死锁并不明显,比如使用线程池),还是试着保证代码质量、监控线程池和避免无限阻塞。我很难想象你情愿无限等待程序运行完成,如同 get() 方法和 block() 方法在没有设定超时时间的情况下执行。

感谢阅读!

原文链接: dzone 翻译: ImportNew.com - 一杯哈希不加盐
译文链接: http://www.importnew.com/30277.html
[ 转载请保留原文出处、译者和译文链接。]

首页 上一页 1 2 3 下一页 尾页 3/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇说说 MQ 之 Kafka(一) 下一篇InnoDB 存储引擎之索引和优化

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目