Java线程池/执行器服务和wait()s - 线程&任务队列?

时间:2012-03-01 10:30:06

标签: java multithreading concurrency threadpool executorservice

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

假设我有一个固定大小的线程池 - 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()在一个对象上进行一些对第三方的I / O调用,并在操作完成时等待回调,因此它自然需要一段时间才能完成。

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

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

4 个答案:

答案 0 :(得分:17)

wait()是阻止操作:

  

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

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

是的,执行程序线程本身进入休眠状态,允许其他线程切换并消耗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) } 

});

这样,外部系统的API只会在结果准备就绪时通知您,您不必等待并阻止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 :(得分:1)

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

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

答案 2 :(得分:0)

我对Tomasz的答案发表评论,但我的声誉不允许(还),抱歉。

我知道这个问题已经过时了,但对于仍然最终阅读此页面的人来说,看看Future,尤其是guava的ListenableFuture,它可以让你注册回调和链接未来,恰恰是目的没有阻塞你的线程(因此将线程释放回池中以供其他人使用它)。

答案 3 :(得分:0)

所有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期间取消未来。如果使用submit()而不是execute(),则InterruptedException & ExecutionException将在框架本身中被吞噬。

相关问题