1,首先提出几个问题:
1.1,子线程中的异常在主线程中是否可以catch
1.2,在spring中主线程有事务,那么子线程中有事务码
2,先看第一个问题
2.1,我们在main方法里面测试,代码如下
package com.pingan.test.call;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class MutiThreadException {
public static void main(String[] args) {
System.out.println("MutiThreadException.main()------>start<------");
try {
List<Future<Object>> resultList = new ArrayList<Future<Object>>();
// ExceptionHandle exceptionHandle = new ExceptionHandle();
ExecutorService service=Executors.newFixedThreadPool(6);
for (int i = 0; i < 3; i++) {
Thread t=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("id "+Thread.currentThread().getName()+"---start>");
if(Thread.currentThread().getName().endsWith("2")){
// int i=1/0;
throw new RuntimeException();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("id "+Thread.currentThread().getName()+"----end:");
}
});
// t.setUncaughtExceptionHandler(new ExceptionHandle());
t.start();
// Future<Object> future = (Future<Object>) service.submit(t);
// resultList.add(future);
}
// for(Future f:resultList){
// Object o=f.get();
// System.out.println(o);
// }
// service.shutdown();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("MutiThreadException.main()------>end<------");
}
}
run后的结果如下
MutiThreadException.main()------>start<------
id Thread-0---start>
id Thread-1---start>
MutiThreadException.main()------>end<------
id Thread-2---start>
Exception in thread "Thread-2" java.lang.RuntimeException
at com.pingan.test.call.MutiThreadException$1.run(MutiThreadException.java:26)
at java.lang.Thread.run(Thread.java:662)
id Thread-0----end:
id Thread-1----end:
可以看出日志打印了异常,是否就说明我们catch到了异常呢,答案:否。我们可以吧catch中的
e.printStackTrace()注释,然后再次运行,会发现结果依然一样。
什么原因呢,其实是异常在子线程中逃逸到了控制台,并没有被主线程catch到。thread的run方法不允许抛出异常,所以我们要在线程里自己处理,对于运行时异常如果没有catch那么就逃逸到控制台。
当然API也提供了一个接口UncaughtExceptionHandler,我们实现就可以然后在set到线程中,代码如下
2.2,我们把测试代码中的这一行注释掉t.setUncaughtExceptionHandler(new ExceptionHandle());
package com.pingan.test.call;
import java.lang.Thread.UncaughtExceptionHandler;
public class ExceptionHandle implements UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("ExceptionHandle.uncaughtException()");
}
}
2.2,我们把测试代码中的这一行注释掉t.setUncaughtExceptionHandler(new ExceptionHandle());
run后的结果如下:
MutiThreadException.main()------>start<------
id Thread-0---start>
id Thread-1---start>
MutiThreadException.main()------>end<------
id Thread-2---start>
ExceptionHandle.uncaughtException()
id Thread-1----end:
id Thread-0----end:
可以看到异常被内部捕获处理掉,但是id Thread-2---end没有打印,说明线程异常后直接结束了,没有走下面的代码。所以我觉得这个接口没有实际的应用场景。
2.3,如果要是用线程池呢
我们注释掉这两行
t.setUncaughtExceptionHandler(new ExceptionHandle());
t.start();
这两行放开注释
Future<Object> future = (Future<Object>) service.submit(t);
service.shutdown();
Run代码运行如下
MutiThreadException.main()------>start<------
MutiThreadException.main()------>end<------
id pool-1-thread-1---start>
id pool-1-thread-2---start>
id pool-1-thread-3---start>
id pool-1-thread-1----end:
id pool-1-thread-3----end:
可以看到控制台没有异常,说明ExecutorService已经处理过了,那我们怎么知道子线程是否有异常呢,代码中我们看到submit是有返回值的,修改测试代码如下
package com.pingan.test.call;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class MutiThreadException {
public static void main(String[] args) {
System.out.println("MutiThreadException.main()------>start<------");
ExecutorService service=null;
try {
List<Future<Object>> resultList = new ArrayList<Future<Object>>();
// ExceptionHandle exceptionHandle = new ExceptionHandle();
service=Executors.newFixedThreadPool(6);
for (int i = 0; i < 3; i++) {
Thread t=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("id "+Thread.currentThread().getName()+"---start>");
if(Thread.currentThread().getName().endsWith("1")){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int i=1/0;
// throw new RuntimeException();
}
if(Thread.currentThread().getName().endsWith("2")){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// int i=1/0;
throw new RuntimeException();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("id "+Thread.currentThread().getName()+"----end:");
}
});
// t.setUncaughtExceptionHandler(new ExceptionHandle());
// t.start();
Future<Object> future = (Future<Object>) service.submit(t);
resultList.add(future);
}
System.out.println("resultList.size:"+resultList.size());
for(Future<Object> f:resultList){
System.out.println("f------>"+f);
Object o=null;
// try {
o=f.get();
// } catch (ExecutionException e) {
// e.printStackTrace();
// }
// System.out.println(o);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
service.shutdown();
}
System.out.println("MutiThreadException.main()------>end<------");
}
}
Run运行结果如下
MutiThreadException.main()------>start<------
id pool-1-thread-1---start>
id pool-1-thread-2---start>
resultList.size:3
id pool-1-thread-3---start>
f------>java.util.concurrent.FutureTask@7d67d940
id pool-1-thread-3----end:
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
at java.util.concurrent.FutureTask.get(FutureTask.java:83)
at com.pingan.test.call.MutiThreadException.main(MutiThreadException.java:63)
Caused by: java.lang.ArithmeticException: / by zero
at com.pingan.test.call.MutiThreadException$1.run(MutiThreadException.java:33)
at java.lang.Thread.run(Thread.java:662)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
at java.lang.Thread.run(Thread.java:662)
MutiThreadException.main()------>end<------
这个里面信息还是比较多的,可以看到线程1和2都没有结束,打印的异常是线程1抛出的异常,线程1一共休眠了3秒,线程2休眠了2秒,为什么线程1的异常打印了而没有打印1的异常呢?为什么?为什么呢?
这里要怪我烦的错误,我们是吧Futrue放到List里面然后遍历获取的,肯定是按顺序获取线程1,2,3的结果的,当获取1的时候f.get方法就抛了异常了,方法直接就结束了。所以我们修改代码吧f.get()方法try/catch住,捕获ExecutionException捕获执行异常,再次运行
MutiThreadException.main()------>start<------
id pool-1-thread-1---start>
id pool-1-thread-2---start>
resultList.size:3
id pool-1-thread-3---start>
f------>java.util.concurrent.FutureTask@2ce83912
id pool-1-thread-3----end:
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
at java.util.concurrent.FutureTask.get(FutureTask.java:83)
at com.pingan.test.call.MutiThreadException.main(MutiThreadException.java:63)
Caused by: java.lang.ArithmeticException: / by zero
at com.pingan.test.call.MutiThreadException$1.run(MutiThreadException.java:33)
at java.lang.Thread.run(Thread.java:662)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)null
f------>java.util.concurrent.FutureTask@4318f375
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
at java.lang.Thread.run(Thread.java:662)
java.util.concurrent.ExecutionException: java.lang.RuntimeException
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
at java.util.concurrent.FutureTask.get(FutureTask.java:83)
at com.pingan.test.call.MutiThreadException.main(MutiThreadException.java:63)
Caused by: java.lang.RuntimeException
at com.pingan.test.call.MutiThreadException$1.run(MutiThreadException.java:43)
at java.lang.Thread.run(Thread.java:662)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
at java.lang.Thread.run(Thread.java:662)
null
f------>java.util.concurrent.FutureTask@1b17a8bd
null
MutiThreadException.main()------>end<------