线程遇到未处理的异常就结束了
这个好理解,当线程出现未捕获异常的时候就执行不下去了,留给它的就是垃圾回收了。
线程池中线程频繁出现未捕获异常
当线程池中线程频繁出现未捕获的异常,那线程的复用率就大大降低了,需要不断地创建新线程。
做个实验:
public class threadexecutor {
private threadpoolexecutor threadpoolexecutor = new threadpoolexecutor(1, 1, 60, timeunit.seconds,
new arrayblockingqueue<>(200), new threadfactorybuilder().setnameformat("customthread %d").build());
@test
public void test() {
intstream.rangeclosed(1, 5).foreach(i -> {
try {
thread.sleep(100);
} catch (interruptedexception e) {
e.printstacktrace();
}
threadpoolexecutor.execute(() -> {
int j = 1/0;
});});
}
}新建一个只有一个线程的线程池,每隔0.1s提交一个任务,任务中是一个1/0的计算。
exception in thread "customthread 0" java.lang.arithmeticexception: / by zero at thread.threadexecutor.lambda$null$0(threadexecutor.java:25) at java.util.concurrent.threadpoolexecutor.runworker(threadpoolexecutor.java:1142) at java.util.concurrent.threadpoolexecutor$worker.run(threadpoolexecutor.java:617) at java.lang.thread.run(thread.java:748) exception in thread "customthread 1" java.lang.arithmeticexception: / by zero at thread.threadexecutor.lambda$null$0(threadexecutor.java:25) at java.util.concurrent.threadpoolexecutor.runworker(threadpoolexecutor.java:1142) at java.util.concurrent.threadpoolexecutor$worker.run(threadpoolexecutor.java:617) at java.lang.thread.run(thread.java:748) exception in thread "customthread 2" java.lang.arithmeticexception: / by zero at thread.threadexecutor.lambda$null$0(threadexecutor.java:25) at java.util.concurrent.threadpoolexecutor.runworker(threadpoolexecutor.java:1142) at java.util.concurrent.threadpoolexecutor$worker.run(threadpoolexecutor.java:617) at java.lang.thread.run(thread.java:748) exception in thread "customthread 3" java.lang.arithmeticexception: / by zero at thread.threadexecutor.lambda$null$0(threadexecutor.java:25) at java.util.concurrent.threadpoolexecutor.runworker(threadpoolexecutor.java:1142) at java.util.concurrent.threadpoolexecutor$worker.run(threadpoolexecutor.java:617) at java.lang.thread.run(thread.java:748) exception in thread "customthread 4" java.lang.arithmeticexception: / by zero at thread.threadexecutor.lambda$null$0(threadexecutor.java:25) at java.util.concurrent.threadpoolexecutor.runworker(threadpoolexecutor.java:1142) at java.util.concurrent.threadpoolexecutor$worker.run(threadpoolexecutor.java:617) at java.lang.thread.run(thread.java:748) exception in thread "customthread 5" java.lang.arithmeticexception: / by zero at thread.threadexecutor.lambda$null$0(threadexecutor.java:25) at java.util.concurrent.threadpoolexecutor.runworker(threadpoolexecutor.java:1142) at java.util.concurrent.threadpoolexecutor$worker.run(threadpoolexecutor.java:617) at java.lang.thread.run(thread.java:748)
可见每次执行的线程都不一样,之前的线程都没有复用。原因是因为出现了未捕获的异常。
把异常捕获试试:
public class threadexecutor {
private threadpoolexecutor threadpoolexecutor = new threadpoolexecutor(1, 1, 60, timeunit.seconds,
new arrayblockingqueue<>(200), new threadfactorybuilder().setnameformat("customthread %d").build());
@test
public void test() {
intstream.rangeclosed(1, 5).foreach(i -> {
try {
thread.sleep(100);
} catch (interruptedexception e) {
e.printstacktrace();
}
threadpoolexecutor.execute(() -> {
try {
int j = 1 / 0;
} catch (exception e) {
system.out.println(thread.currentthread().getname() +" "+ e.getmessage());
}
});
});
}
}customthread 0 / by zero customthread 0 / by zero customthread 0 / by zero customthread 0 / by zero customthread 0 / by zero
可见当异常捕获了,线程就可以复用了。
问题来了,代码中异常不可能全部捕获
如果要捕获那些没被业务代码捕获的异常,可以设置thread类的uncaughtexceptionhandler属性。这时使用threadfactorybuilder会比较方便,threadfactorybuilder是guava提供的threadfactory生成器。
new threadfactorybuilder()
.setnameformat("customthread %d")
.setuncaughtexceptionhandler((t, e) -> system.out.println(t.getname() + "发生异常" + e.getcause()))
.build()修改之后:
public class threadexecutor {
private static threadpoolexecutor threadpoolexecutor = new threadpoolexecutor(1, 1, 60, timeunit.seconds,
new arrayblockingqueue<>(200),
new threadfactorybuilder()
.setnameformat("customthread %d")
.setuncaughtexceptionhandler((t, e) -> system.out.println("uncaughtexceptionhandler捕获到:" + t.getname() + "发生异常" + e.getmessage()))
.build());
@test
public void test() {
intstream.rangeclosed(1, 5).foreach(i -> {
try {
thread.sleep(100);
} catch (interruptedexception e) {
e.printstacktrace();
}
threadpoolexecutor.execute(() -> {
system.out.println("线程" + thread.currentthread().getname() + "执行");
int j = 1 / 0;
});
});
}
}线程customthread 0执行 uncaughtexceptionhandler捕获到:customthread 0发生异常/ by zero 线程customthread 1执行 uncaughtexceptionhandler捕获到:customthread 1发生异常/ by zero 线程customthread 2执行 uncaughtexceptionhandler捕获到:customthread 2发生异常/ by zero 线程customthread 3执行 uncaughtexceptionhandler捕获到:customthread 3发生异常/ by zero 线程customthread 4执行 uncaughtexceptionhandler捕获到:customthread 4发生异常/ by zero
可见,结果并不是想象的那样,线程池中原有的线程没有复用!所以通过uncaughtexceptionhandler想将异常吞掉使线程复用这招貌似行不通。它只是做了一层异常的保底处理。
将excute改成submit试试
public class threadexecutor {
private static threadpoolexecutor threadpoolexecutor = new threadpoolexecutor(1, 1, 60, timeunit.seconds,
new arrayblockingqueue<>(200),
new threadfactorybuilder()
.setnameformat("customthread %d")
.setuncaughtexceptionhandler((t, e) -> system.out.println("uncaughtexceptionhandler捕获到:" + t.getname() + "发生异常" + e.getmessage()))
.build());
@test
public void test() {
intstream.rangeclosed(1, 5).foreach(i -> {
try {
thread.sleep(100);
} catch (interruptedexception e) {
e.printstacktrace();
}
future<?> future = threadpoolexecutor.submit(() -> {
system.out.println("线程" + thread.currentthread().getname() + "执行");
int j = 1 / 0;
});
try {
future.get();
} catch (interruptedexception e) {
e.printstacktrace();
} catch (executionexception e) {
e.printstacktrace();
}
});
}
}线程customthread 0执行 java.util.concurrent.executionexception: java.lang.arithmeticexception: / by zero 线程customthread 0执行 java.util.concurrent.executionexception: java.lang.arithmeticexception: / by zero 线程customthread 0执行 java.util.concurrent.executionexception: java.lang.arithmeticexception: / by zero 线程customthread 0执行 java.util.concurrent.executionexception: java.lang.arithmeticexception: / by zero 线程customthread 0执行 java.util.concurrent.executionexception: java.lang.arithmeticexception: / by zero
通过submit提交线程可以屏蔽线程中产生的异常,达到线程复用。当get()执行结果时异常才会抛出。
原因是通过submit提交的线程,当发生异常时,会将异常保存,待future.get();时才会抛出。
这是futuretask的部分run()方法,看setexception:
public void run() {
try {
callable<v> c = callable;
if (c != null && state == new) {
v result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (throwable ex) {
result = null;
ran = false;
setexception(ex);
}
if (ran)
set(result);
}
}
}
protected void setexception(throwable t) {
if (unsafe.compareandswapint(this, stateoffset, new, completing)) {
outcome = t;
unsafe.putorderedint(this, stateoffset, exceptional); // final state
finishcompletion();
}
}将异常存在outcome对象中,没有抛出,再看get方法:
public v get() throws interruptedexception, executionexception {
int s = state;
if (s <= completing)
s = awaitdone(false, 0l);
return report(s);
}
private v report(int s) throws executionexception {
object x = outcome;
if (s == normal)
return (v)x;
if (s >= cancelled)
throw new cancellationexception();
throw new executionexception((throwable)x);
}当outcome是异常时才抛出。
总结
1、线程池中线程中异常尽量手动捕获
2、通过设置threadfactory的uncaughtexceptionhandler可以对未捕获的异常做保底处理,通过execute提交任务,线程依然会中断,而通过submit提交任务,可以获取线程执行结果,线程异常会在get执行结果时抛出。
以上就是java线程池执行过程中遇到异常的原因和解决办法的详细内容,更多关于java线程池执行遇到异常的资料请关注代码网其它相关文章!
发表评论