ThreadPoolExecutorの罠
次のプログラムに何か問題がありますか?
注意、私は間違った宿題を提出しました:new DivideNumbersJob(4,0).地球人はすべて知っていて、除数は0ではありません.通常、この作業はArithmeticExceptionを投げ出すはずです.しかし、これはRuntimeExceptionであり、メソッドのthrowsの一部で宣言する必要はありません.したがって、DivideNumbersJobというクラスは依然として「Runnableインタフェースを実現した」(throws ArithmeticExceptionを加えると)コンパイルは通じないと言える.インタフェースのthrows部分と一致しないためである.
ただし、実際の実行結果は以下の通りです.
参照
6/2 === 3 mod 0
100/3 === 33 mod 1
12/5 === 2 mod 2
何の異常もない!何の異常もない!!コンソールがきれい!!!
なんと恐ろしいことか.私はプログラムが異常を投げ出すことを恐れないで、しかし、私が最も恐れているのは、異常を投げ出したのに、静かに抜け出して、bugを静かに潜伏させています.
「Pythonの禅」には「Error should never pass silently,unless explicitly silenced.」という言葉がある.参照先:
http://www.python.org/dev/peps/pep-0020/
答え:この異常は事実上捕獲され、Futureオブジェクトに格納されます.
mainメソッドを少し変更します.
私たちは寝た後、完成していない場合は、宿題をキャンセルします.完了したら、その値を取得してみます.submitはRunnableジョブをコミットし、戻り値はnullに違いないことを知っています.ただし、ジョブの実行中に異常がある場合は、f.get()メソッドを呼び出して値を取得しようとすると、ExecutionExceptionが放出されます.
これは、現在実行されている結果です.
参照
12/5 === 2 mod 2
6/2 === 3 mod 0
100/3 === 33 mod 1
Exception in thread "main"java.util.concurrent.ExecutionException: java.lang.ArithmeticException:/by zero
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:252)
at java.util.concurrent.FutureTask.get(FutureTask.java:111)
at tpe2.TPEDemo.main(TPEDemo.java:44)
Caused by: java.lang.ArithmeticException:/by zero
at tpe2.DivideNumbersJob.run(TPEDemo.java:15)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:636)
最後のExecutionExceptionはまさにFutureです.get()が投げ出した.そのネストされたcauseは私たちのDivideNumbersJobです.run()から投げ出されたArithmeticException.このオブジェクトはExceptionを使用できます.getCauseメソッドが取得されます.
また、Futureオブジェクトを使用するのは、ジョブの終了を容易にするためだけであり、Futureを呼び出したくない場合がある.get()メソッドは例外を取得します.この場合、いっそ異常をRunnableに置く.run()では、夜の夢が多くならないように徹底的に解決します.
これにより、ArithmeticExceptionが発生すると、すぐに異常が印刷されます.このような致命的な異常もこれ以上処理できない.
なお、ジョブがExecutorである場合は.execute(Runnable r);コミットされると、Futureオブジェクトがないため、この例外は格納されず、実行が完了するとExecutorに投げ出されます.
package tpe2;
import java.util.concurrent.*;
/**
* , 。
*/
class DivideNumbersJob implements Runnable {
int a,b;
public DivideNumbersJob(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public void run() {
int c = a/b;
int d = a%b;
System.out.format("%d / %d === %d mod %d
", a, b, c, d);
}
}
public class TPEDemo {
public static void main(String[] args) throws Exception {
ThreadPoolExecutor e = new ThreadPoolExecutor(5, 5, 0,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
// , , ,
// submit , Future 。
Future<?> f1 = e.submit(new DivideNumbersJob(6, 2));
Future<?> f2 = e.submit(new DivideNumbersJob(12, 5));
Future<?> f3 = e.submit(new DivideNumbersJob(4, 0)); // !
Future<?> f4 = e.submit(new DivideNumbersJob(100, 3));
// Executor, 。
e.shutdown();
//
Thread.sleep(2000);
// Future , , 。
for (Future<?> f : new Future<?>[]{f1,f2,f3,f4}) {
if(!f.isDone()) {
f.cancel(true);
}
}
}
}
注意、私は間違った宿題を提出しました:new DivideNumbersJob(4,0).地球人はすべて知っていて、除数は0ではありません.通常、この作業はArithmeticExceptionを投げ出すはずです.しかし、これはRuntimeExceptionであり、メソッドのthrowsの一部で宣言する必要はありません.したがって、DivideNumbersJobというクラスは依然として「Runnableインタフェースを実現した」(throws ArithmeticExceptionを加えると)コンパイルは通じないと言える.インタフェースのthrows部分と一致しないためである.
ただし、実際の実行結果は以下の通りです.
参照
6/2 === 3 mod 0
100/3 === 33 mod 1
12/5 === 2 mod 2
何の異常もない!何の異常もない!!コンソールがきれい!!!
なんと恐ろしいことか.私はプログラムが異常を投げ出すことを恐れないで、しかし、私が最も恐れているのは、異常を投げ出したのに、静かに抜け出して、bugを静かに潜伏させています.
「Pythonの禅」には「Error should never pass silently,unless explicitly silenced.」という言葉がある.参照先:
http://www.python.org/dev/peps/pep-0020/
答え:この異常は事実上捕獲され、Futureオブジェクトに格納されます.
mainメソッドを少し変更します.
//
Thread.sleep(2000);
// Future , , 。
for (Future<?> f : new Future<?>[]{f1,f2,f3,f4}) {
if(!f.isDone()) {
f.cancel(true);
+ } else {
+ Object result = f.get();
}
}
私たちは寝た後、完成していない場合は、宿題をキャンセルします.完了したら、その値を取得してみます.submitはRunnableジョブをコミットし、戻り値はnullに違いないことを知っています.ただし、ジョブの実行中に異常がある場合は、f.get()メソッドを呼び出して値を取得しようとすると、ExecutionExceptionが放出されます.
これは、現在実行されている結果です.
参照
12/5 === 2 mod 2
6/2 === 3 mod 0
100/3 === 33 mod 1
Exception in thread "main"java.util.concurrent.ExecutionException: java.lang.ArithmeticException:/by zero
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:252)
at java.util.concurrent.FutureTask.get(FutureTask.java:111)
at tpe2.TPEDemo.main(TPEDemo.java:44)
Caused by: java.lang.ArithmeticException:/by zero
at tpe2.DivideNumbersJob.run(TPEDemo.java:15)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:636)
最後のExecutionExceptionはまさにFutureです.get()が投げ出した.そのネストされたcauseは私たちのDivideNumbersJobです.run()から投げ出されたArithmeticException.このオブジェクトはExceptionを使用できます.getCauseメソッドが取得されます.
また、Futureオブジェクトを使用するのは、ジョブの終了を容易にするためだけであり、Futureを呼び出したくない場合がある.get()メソッドは例外を取得します.この場合、いっそ異常をRunnableに置く.run()では、夜の夢が多くならないように徹底的に解決します.
@Override
public void run() {
+ try {
int c = a/b;
int d = a%b;
System.out.format("%d / %d === %d mod %d
", a, b, c, d);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
}
これにより、ArithmeticExceptionが発生すると、すぐに異常が印刷されます.このような致命的な異常もこれ以上処理できない.
なお、ジョブがExecutorである場合は.execute(Runnable r);コミットされると、Futureオブジェクトがないため、この例外は格納されず、実行が完了するとExecutorに投げ出されます.