Java Thread Pools/Executor Service and wait()s - 线程和任务队列会发生什么变化?



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

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

我有一些代码:

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

让我们假设上面的代码被调用了100次。池中只有5个线程(因此上面的语句中只有5条应该同时存在)。还假设wait()在一个对象上对第三方进行一些I/O调用,并在操作完成时等待回调,因此自然需要一段时间才能完成。

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

如果正在等待的任务进入睡眠状态,那么当它获得notify()并醒来时会发生什么?线程是否返回线程池的队列(在前面或后面),并等待5个线程中的一个可以继续执行它(即调用doSomethingelse())?或者执行它的线程也进入睡眠状态,即5个执行器线程中的一个线程坐在那里等待任务(这就是我所假设的)?还是执行器线程在等待()返回第一个任务时,拿起另一个任务并简单地被中断?

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()方法。

wait()无法了解胎面池的任何信息。并且线程池不能知道关于wait()的任何信息。因此,他们无论如何都无法互动。

它们照常工作——wait()只是一个长时间运行的阻塞操作,线程池只是在有限的线程池上运行的可运行程序的队列。

我想对Tomasz的回答发表评论,但我的声誉还不允许,对不起。

我知道这个问题已经过时了,但对于那些最终仍在阅读本页的人来说,看看Future,尤其是番石榴的ListenableFuture,它可以让你注册回调并将Future链接在一起,确切地说,目的是不阻塞你的线程(从而将线程释放回池中供其他人使用)。

所有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将被框架本身吞噬。

最新更新