我环顾四周,但没有找到答案,所以我想确认一下。
假设我有一个固定大小的线程池-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
将被框架本身吞噬。