首页 文章

Java线程池/ Actuator 服务和wait() - 线程和任务队列会发生什么?

提问于
浏览
15

我环顾四周但没有找到答案,所以我想确认一下 .

假设我有一个固定大小的线程池 - ExecutorService pool = Executors.newFixedThreadPool(5);

我有一些代码:

pool.execute(new Runnable(){
    try{
        Object waitForMe = doSomethingAndGetObjectToWaitFor();
        waitForMe.wait();
        doSomethingElse();
    }catch(Exception e){ throw new RunTimeException(e) } 

});

让我们假设上面的代码被称为几百次 . 池中只有5个线程(因此上述语句中只有5个应该在一个点上存在) . 还假设 wait() 在一个对象上,对一个thrid方进行一些I / O调用,并在操作完成时等待回调,因此它自然需要一段时间才能完成 .

现在我的问题是当其中一个任务到达 wait() 时的行为是什么,该任务是否进入休眠状态,然后线程池中的线程将另一个任务从队列中取出并开始运行它?

如果等待的任务进入睡眠状态,当它获得 notify() 并醒来时会发生什么?线程是否返回到线程池的队列(前面或后面)并等待,直到5个线程中的一个可以继续执行它(即调用 doSomethingelse() )?或者正在执行它的线程是否也进入休眠状态,即5个执行程序线程中的一个正在等待任务(这是我假设的)?或者执行程序线程是否接受另一个任务,并在第一个任务从wait()返回时被中断?

4 回答

  • 0

    wait()是一个阻塞操作:

    导致当前线程等待,直到另一个线程调用notify()方法或notifyAll()

    这意味着池中的线程将等待,但从外部看起来当前任务需要花费很多时间才能完成 . 这也意味着如果执行5个任务并且它们都是 wait() ,则 Executor 无法处理剩余的任务,ekhem在队列中等待 .

    确实,执行程序线程本身进入休眠状态,允许其他线程切换并消耗CPU(因此您可以让数百个线程同时等待并且您的系统仍然响应)但仍然线程“无法使用”并被阻止 .

    另一个有趣的功能是中断 - 如果线程等待某事或睡眠,你可以打断它 . 请注意, wait()Thread.sleep()都声明InterruptedException . 使用ExecutorService,您可以通过简单地调用来利用它:future.cancel()future 是将任务提交到 ExecutorService 时获得的对象) .

    最后我想你应该重新设计你的解决方案 . 而不是主动等待外部系统完成,提供带回调的API:

    pool.execute(new Runnable(){
        try{
            doSomethingAndCallMeBackWhenItsDone(new Callback() {
                public void done() {
                    doSomethingElse();
                }
            });
        }catch(Exception e){ throw new RunTimeException(e) } 
    
    });
    

    这样外部系统's API will simply notify you when the results are ready and you won' t必须等待并阻止 ExecutorService . 最后,如果 doSomethingElse() 需要花费很多时间,您甚至可能决定安排它,而不是使用外部第三方I / O线程:

    pool.execute(new Runnable(){
        try{
            doSomethingAndCallMeBackWhenItIsDone(new Callback() {
                public void done() {
                    pool.submit(new Callbale<Void>() {
                        public Void call() {
                            doSomethingElse();
                        }
                    }
                }
            });
        }catch(Exception e){ throw new RunTimeException(e) } 
    
    });
    

    更新:您在询问如何处理超时?这是我的想法:

    pool.execute(new Runnable(){
        try{
            doSomethingAndCallMeBackWhenItsDone(new Callback() {
                public void done() {
                    doSomethingElse();
                }
                public void timeout() {
                    //opps!
                }
            });
        }catch(Exception e){ throw new RunTimeException(e) } 
    
    });
    

    我想你可以在第三方实现超时,如果在那里发生超时,只需调用 timeout() 方法 .

  • 1

    wait() 对胎面池一无所知 . 并且线程池对 wait() 一无所知 . 所以无论如何他们都无法互动 .

    它们像往常一样工作 - wait() 只是一个长时间运行的阻塞操作,线程池只是在有限的线程池上运行的runnables队列 .

  • 0

    我会评论Tomasz的答案,但我的声誉不允许(还),抱歉 .

    我知道这个问题已经过时了,但对于那些仍然最终阅读本页的人来说,看看Future,特别是guava的ListenableFuture,它可以让你注册回调和链接未来,恰恰是不阻塞你的线程的目的(和从而将线程释放回池中以供其他人使用它 .

  • 16

    将阻止所有5个线程,并且应用程序将处于非 生产环境 状态 .

    添加到 Tomasz 回答,我想实现如下的超时机制 .

    Future<Long> futureResult = service.execute(myCallable);
                Long result = null;
                try{
                    result = futureResult.get(5000, TimeUnit.MILLISECONDS);
                }catch(TimeoutException e){
                    System.out.println("Time out after 5 seconds");
                    futureResult.cancel(true);
                }catch(InterruptedException ie){
                    System.out.println("Error: Interrupted");
                }catch(ExecutionException ee){
                    System.out.println("Error: Execution interrupted");
                }
    

    除了 TimeoutException ,您可以在 InterruptedException & ExecutionException 期间取消Future . 如果使用submit()而不是execute(),则 InterruptedException & ExecutionException 将在框架本身中被吞下 .

相关问题