将可调用对象中的可调用对象提交给执行程序服务会导致程序挂起

问题描述 投票:0回答:2

目前我一直在使用

FixedThreadPool
来细分一个大任务;这一切都运行良好。然而;我现在发现其中一项任务的一部分本身可以细分。我曾经尝试向
FixedThreadPool
提交更多 Callables,但程序挂在
Future#get()
上(在代码中标记)

以下程序复制了该问题(我使用了尺寸为 1 的

FixedThreadPool
使问题变得更糟)

public class ThreadPoolTest {
    static ExecutorService threadPool=Executors.newFixedThreadPool(1);

    //method run by inner callable
    public void printText(){
        try {
            Thread.sleep(100);
        } catch (InterruptedException ex) {
            Logger.getLogger(ThreadPoolTest.class.getName()).log(Level.SEVERE, null, ex);
        }
        System.out.println("Printed from within thread");
    }

    //method run by outer callable
    public void testThreadPool(){

        Callable<Void> printOnAThread=()->{printText(); return null; };

        Future<Void> f2=threadPool.submit(printOnAThread);

        try {    
            System.out.println("Called");
            f2.get(); //<--hangs here
        } catch (InterruptedException | ExecutionException ex) {
            Logger.getLogger(ThreadPoolTest.class.getName()).log(Level.SEVERE, null, ex);
            throw new RuntimeException("Failed to print in thread", ex);
        }
    }


    public static void testThreadWithinThread(){
        ThreadPoolTest t=new ThreadPoolTest();

        Callable<Void> testCallable=()->{t.testThreadPool();return null;};

        Future<Void> f=threadPool.submit(
            testCallable
        );

        try {
            f.get();
        } catch (InterruptedException | ExecutionException ex) {
            Logger.getLogger(ThreadPoolTest.class.getName()).log(Level.SEVERE, null, ex);
            throw new RuntimeException("Main thread failed", ex);
        }
    }

    public static void main(String[] args){
        testThreadWithinThread();
        threadPool.shutdown();
        System.out.println("Program exits");
    }
}

我期望发生什么

  1. 第一个线程已提交。
    testThreadWithinThread()
    奔跑
  2. testThreadWithinThread()
    提交可调用 (
    ()->{t.testThreadPool();return null;};
    )
  3. 提交的可调用文件“立即”开始运行
  4. t.testThreadPool();开始运行
  5. testThreadPool();
    本身提交了一个内部可调用的
    ()->{printText(); return null; };
  6. 内部可调用尚无法运行,因为线程池不空闲
  7. 到达
  8. f.get();
    ,外部可调用块并等待。这会释放
    FixedThreadPool
  9. 中的线程
  10. 内部可调用现在运行完成
  11. f2.get();
    不再阻塞,外部可调用运行至完成

到底发生了什么

步骤 1-6 正如我预期的那样发生,但是在第 7 点,外部可调用对象被阻止;由于某种原因,它没有释放线程,因此程序挂起。

问题

为什么程序此时挂起?有什么方法可以安全地从可调用对象中提交可调用对象吗?

java multithreading threadpool
2个回答
2
投票

您的线程池包含 1 个线程。它一次只能执行一个可调用/可运行。所有其他提交的任务都会排队,直到有线程可以执行它们为止。

增加泳池大小。


0
投票

我目前正在自己解决同样的问题,我将在尝试这个问题时给出一些我的观察结果......

您可以创建 Callable 的包装对象,其中包含对 ExecutorService 的引用。现在,您可以让该 Callable 也向 ExecutorService 提交一个新的 Callable。

如果父 Callable 不依赖于子 Callable 的完成,那么这实际上与使用两个完全不相关的构造将 Callable 提交到同一个 ExecutorService 引用没有什么不同。 ExecutorService 最终将完成这两个任务 - 一切如常。

但是,如果其中一些 Callable 等待(即在返回的

.get()
上调用
Future<V>
)他们已提交的 Callable,事情会变得更加复杂。因为显然你现在有一个线程正在等待另一个线程完成。这本质上并不是一个糟糕的情况,但它取决于很多因素......每个 Callable 需要多长时间?你将有多少个线程?线程可以有超时吗?您需要处理的传入有效负载量是多少?

例如,2 个线程,三个级联 Callable:

  • Callable(1):提交新的Callable(2),谁在等待它 完成
  • Callable(2):提交新的Callable(3),谁在等待 就完成了
  • Callable (3):尝试提交新的 Callable,但没有可用线程。

你现在面临的是教科书般的僵局。

© www.soinside.com 2019 - 2024. All rights reserved.