美文网首页
记一次线程饥饿死锁的BUG

记一次线程饥饿死锁的BUG

作者: 狒狒_94d7 | 来源:发表于2020-04-10 20:27 被阅读0次

Executors.newFixedThreadPool(threads)通过设定相同的核心线程数和最大线程数以及无界的任务队列来实现固定线程数的线程池。由于可用线程数固定,当没有空闲线程来执行新任务时,会提交到任务队列里面等待线程空闲时执行。
我所遇到的问题场景是执行一个异步任务,通过固定线程数的线程池来提交任务,任务本身需要并发处理多个子任务,也复用这个线程池来处理了,简化版代码如下:

ExecutorService service = Executors.newFixedThreadPool(2);//将固定线程数改小方便测试
    
    public void test() throws Throwable
    {
        Future<Integer> future = service.submit(()->{
            List<Callable<Integer>> tasks = new ArrayList<Callable<Integer>>();
            for (int i = 0; i < 10; i++)
            {
                tasks.add(()->{return 1;});
            }
            List<Future<Integer>> futures = service.invokeAll(tasks);
            return futures.stream().mapToInt(f->{
                try {
                    return f.get();
                } catch (Exception e) {
                }
                return 0;
            }).sum();
        });
        
        System.err.println(future.get());
    }

如果一次只有单线程执行test方法,那么执行流程是没有问题。而如果多线程同时执行test方法,那么就有可能发生线程饥饿死锁,原因如下:

  1. 线程1往线程池中提交了任务,假设此时线程池有两个空闲线程,则会有一个线程用来执行submit方法提交任务。
  2. 在上面提交的任务还没执行到invokerAll时,线程2也提交了任务,线程池中另一个线程用来执行线程2submit的任务了。
  3. 此时线程池中两个线程都被占用,所以两次执行到invokeAll的子任务都会被放入线程池的任务队列等待空闲线程来执行。
  4. 但是invokeAll本身是阻塞获取结果的,也就是,只有子任务tasks都执行完毕,submit提交的总任务才能完成。这样一来就形成了子任务tasks在等待线程池中空闲线程来执行,而另一方面,线程池又必需等待tasks执行完成才有空闲线程出来,造成了线程饥饿死锁,任务进行不下去。

问题的根本原因是执行线程数固定,父任务把线程消耗完时,子任务无法执行,而父任务又是等待子任务执行完成才能完成。解决的方法有两个,一是父任务和子任务通过不同的线程池来执行,避免相互影响。二是使用更合适的ForkJoinPool来代替FixedThreadPool来执行这种嵌套任务。

相关文章

  • 记一次线程饥饿死锁的BUG

    Executors.newFixedThreadPool(threads)通过设定相同的核心线程数和最大线程数以及...

  • 死锁

    线程饥饿死锁 锁顺序死锁 动态锁顺序死锁通过锁顺序来避免死锁 避免死锁

  • 关于主线程执行同步任务造成死锁的思考

    经典问题重现 bug 粘贴主线程+同步造成死锁 要想明白,为什么造成死锁,首先要搞懂主线程是干嘛的 主线程程序运行...

  • 高并发编程-05-活跃性问题

    死锁,饥饿,活锁 1,死锁 多个线程,各自占对方的资源,都不愿意释放,从而造成死锁 工具:使用jconsole可以...

  • 线程饥饿死锁

    《Java并发编程实践》中对线程饥饿死锁的解释是这样的:在使用线程池执行任务时,如果任务依赖于其他任务,那么就可能...

  • JavaEE面试题总结 Day39 2018-12-29

    什么是线程死锁?死锁如何产生?如何避免线程死锁? 死锁的介绍: 线程死锁是指由于两个或者多个线程互相持有对方所需要...

  • Java并发编程实战

    1、线程池的数量2、任务独立时,设置线程池的工作队列界限才合理,如果任务之间存在依赖性,则可能导致线程“饥饿死锁”...

  • java多线程中的死锁、活锁、饥饿、无锁都是什么鬼?

    死锁、活锁、饥饿是关于多线程是否活跃出现的运行阻塞障碍问题,如果线程出现了这三种情况,即线程不再活跃,不能再正常地...

  • 死锁、活锁、饥饿锁、无锁

    死锁、活锁、饥饿是关于多线程是否活跃出现的运行阻塞障碍问题,如果线程出现了这三种情况,即线程不再活跃,不能再正常地...

  • Python中的各种锁

    Python之死锁 死锁分为两种情况,多进程/线程的死锁.或者是单线程的死锁. 1.首先看一下单线程的死锁,单线程...

网友评论

      本文标题:记一次线程饥饿死锁的BUG

      本文链接:https://www.haomeiwen.com/subject/igydmhtx.html