本文因生产环境线程池某些场景下的任务异常后,日志文件中没有被记录进来产生的困惑引发的思考。
当然如果所有异步的业务方法run里面都加上一层try…catch…就可以主动捕获所有的异常,也能够记录到日志文件中,然而总有一些人总有一些时候不小心漏掉了,今天分享下run方法如果不加try…catch…的后果
//测试代码
public static void testExecute() {
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 1, 0, TimeUnit.DAYS,
new LinkedBlockingQueue<>(), new MyThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 5; i++) {
int finalI = i;
pool.execute(() -> {
if (finalI == 3) {
throw new RuntimeException("abcdefg");
}
logger.info("lzc" + finalI);
});
}
}
//测试结果 控制台输出
2020-06-04 00:00:50.118 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 91 lzc0
2020-06-04 00:00:50.122 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 91 lzc1
2020-06-04 00:00:50.122 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 91 lzc2
2020-06-04 00:00:51.837 INFO pool-1-thread-2 com.alioo.pool.ExceptionPoolDemo 91 lzc4
Exception in thread "pool-1-thread-1"
java.lang.RuntimeException: abcdefg
at com.alioo.pool.ExceptionPoolDemo.lambda$testExecute$2(ExceptionPoolDemo.java:89)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
//测试结果 日志文件输出
2020-06-04 00:00:50.118 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 91 lzc0
2020-06-04 00:00:50.122 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 91 lzc1
2020-06-04 00:00:50.122 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 91 lzc2
2020-06-04 00:00:51.837 INFO pool-1-thread-2 com.alioo.pool.ExceptionPoolDemo 91 lzc4
1.控制台会有异常堆栈信息 , 然而日志文件中却没有记录这条异常,这个很致命,异步处理的线程出现了异常,日志没有记录事件,事后很难排查
2.corePoolSize设置的是1 , blockinqueue使用的是无界队列,正常情况下,始终只会有一个线程来所有任务,即pool-1-thread-1,然而从上面的日志可以发现有新线程pool-1-thread-2参加工作了
//测试代码
public static void testSubmit() {
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 1, 0, TimeUnit.DAYS,
new LinkedBlockingQueue<>(), new MyThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 5; i++) {
int finalI = i;
pool.submit(() -> {
if (finalI == 3) {
throw new RuntimeException("abcdefg");
}
logger.info("lzc" + finalI);
});
}
}
//测试结果 控制台输出
2020-06-04 00:15:25.069 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 112 lzc0
2020-06-04 00:15:25.072 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 112 lzc1
2020-06-04 00:15:25.072 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 112 lzc2
2020-06-04 00:15:25.073 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 112 lzc4
//测试结果 日志文件输出
2020-06-04 00:15:25.069 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 112 lzc0
2020-06-04 00:15:25.072 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 112 lzc1
2020-06-04 00:15:25.072 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 112 lzc2
2020-06-04 00:15:25.073 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 112 lzc4
1.这个更要命,控制台与日志文件都没有记录这条异常,后果同上
2.submit时没有使用新工作线程,使用始终使用的是pool-1-thread-1
//测试代码
public static void testSubmitFuture() {
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 1, 0, TimeUnit.DAYS,
new LinkedBlockingQueue<>(), new MyThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
List list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
int finalI = i;
Future f = pool.submit(() -> {
if (finalI == 3) {
throw new RuntimeException("abcdefg");
}
logger.info("lzc" + finalI);
});
list.add(f);
}
for (Future f : list) {
try {
f.get();
} catch (Exception e) {
logger.error("出现了异常", e);
}
}
}
//测试结果 控制台输出
2020-06-04 00:18:55.784 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 134 lzc0
2020-06-04 00:18:55.787 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 134 lzc1
2020-06-04 00:18:55.787 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 134 lzc2
2020-06-04 00:18:55.787 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 134 lzc4
2020-06-04 00:18:55.790 ERROR main com.alioo.pool.ExceptionPoolDemo 144 出现了异常
java.util.concurrent.ExecutionException: java.lang.RuntimeException: abcdefg
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at com.alioo.pool.ExceptionPoolDemo.testSubmitFuture(ExceptionPoolDemo.java:142)
at com.alioo.pool.ExceptionPoolDemo.main(ExceptionPoolDemo.java:153)
Caused by: java.lang.RuntimeException: abcdefg
at com.alioo.pool.ExceptionPoolDemo.lambda$testSubmitFuture$4(ExceptionPoolDemo.java:132)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
at java.util.concurrent.FutureTask.run(FutureTask.java)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
//测试结果 日志文件输出
2020-06-04 00:18:55.784 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 134 lzc0
2020-06-04 00:18:55.787 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 134 lzc1
2020-06-04 00:18:55.787 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 134 lzc2
2020-06-04 00:18:55.787 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 134 lzc4
2020-06-04 00:18:55.790 ERROR main com.alioo.pool.ExceptionPoolDemo 144 出现了异常
java.util.concurrent.ExecutionException: java.lang.RuntimeException: abcdefg
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at com.alioo.pool.ExceptionPoolDemo.testSubmitFuture(ExceptionPoolDemo.java:142)
at com.alioo.pool.ExceptionPoolDemo.main(ExceptionPoolDemo.java:153)
Caused by: java.lang.RuntimeException: abcdefg
at com.alioo.pool.ExceptionPoolDemo.lambda$testSubmitFuture$4(ExceptionPoolDemo.java:132)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
at java.util.concurrent.FutureTask.run(FutureTask.java)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
1.future.get()表示接收返回值,当调用future.get()后,控制台与日志文件均记录下来了,完美!
2.submit时没有使用新工作线程,使用始终使用的是pool-1-thread-1
线程池的工作线程类Worker实现了Runnable接口,接下来主要分析Worker的构造方法与run方法
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this);
}
解码:
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
public void uncaughtException(Thread t, Throwable e) {
if (parent != null) {
parent.uncaughtException(t, e);
} else {
Thread.UncaughtExceptionHandler ueh =
Thread.getDefaultUncaughtExceptionHandler();
if (ueh != null) {
ueh.uncaughtException(t, e);
} else if (!(e instanceof ThreadDeath)) {
System.err.print("Exception in thread \""
+ t.getName() + "\" ");
e.printStackTrace(System.err);
}
}
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run(); //这里是我们自己写的业务方法,业务逻辑都在这里
} catch (RuntimeException x) {
thrown = x; throw x; //第1层try...块,业务逻辑会抛出RuntimeException,所以会进入到这里,这里虽然捕获了,然而又throw了
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally { //第2层try...块,这里的try...块没有catch,是不是很惊喜,收到第1个层throw出现的RuntimeException只需要做finally逻辑,然后RuntimeException继续往外抛
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally { //第3层try...块,这里的try...块依旧没有catch,收到第2层throw出现的RuntimeException依旧只需要做finally逻辑,继续往外抛RuntimeException
//然而已经到最外层了,还没有被catch住的话就会交给Thread.currentThread().getThreadGroup().uncaughtException进行处理了。
processWorkerExit(w, completedAbruptly);
}
}
前面已经铺垫了Worker.run()方法代码差异造成了submit后异常处理的不同,接下来具体分析下吧
public Future> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
protected RunnableFuture newTaskFor(Runnable runnable, T value) {
return new FutureTask(runnable, value);
}
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call(); //原始的业务方法call()会在这里进行调用
ran = true;
} catch (Throwable ex) {
result = null; //第1层try...块,这里的try...块遇到RuntimeException直接吃掉了,并没有继续往外抛
ran = false;
setException(ex); //进入这个方法会将ex赋值到outcome对象中(注意正常情况下outcome存放的是方法返回值result)
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING) //第2层try...块,当出现异常时,这里的if不会进去执行
handlePossibleCancellationInterrupt(s);
}
}
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
前面的测试结论中可以看到只调用submit时异常信息是没有任何输出的,而当调用了future.get()异常就出来了,这是为什么呢,下面对着源码来揭晓答案
throw new ExecutionException((Throwable)x);
,好了,这就是只调用submit不会输出异常,而当进一步调用future.get()就会触发往外抛异常了
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
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);
}
logger.error("出现了异常", e);
for (Future f : list) {
try {
f.get();
} catch (Exception e) {
logger.error("出现了异常", e);
}
}
2020-06-04 00:18:55.790 ERROR main com.alioo.pool.ExceptionPoolDemo 144 出现了异常
java.util.concurrent.ExecutionException: java.lang.RuntimeException: abcdefg
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at com.alioo.pool.ExceptionPoolDemo.testSubmitFuture(ExceptionPoolDemo.java:142)
at com.alioo.pool.ExceptionPoolDemo.main(ExceptionPoolDemo.java:153)
Caused by: java.lang.RuntimeException: abcdefg
at com.alioo.pool.ExceptionPoolDemo.lambda$testSubmitFuture$4(ExceptionPoolDemo.java:132)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
at java.util.concurrent.FutureTask.run(FutureTask.java)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
附文中使用了自定义的ThreadFactory,实现与Executors.defaultThreadFactory()一模一样
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
public class MyThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
public MyThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
上面分别分析了execute,submit在业务方法未捕获异常时的表现,当异常发生时不能记录到日志文件中后续定位问题是非常麻烦的,那么作为线程框架可以有哪些解决手段呢?
runWorker()
方法中会调用afterExecute(task, thrown)
,可以在重写这个方法对异常进行日志记录这里需要说明的是重写了afterExecute(task, thrown)
只是增加了对异常的日志打印,但是异常还是会继续往外抛出,所以控制台会打印2次异常日志,但是日志文件只会打印1次(未重写afterExecute时,控制台打印1次,日志文件0次)
//测试代码
public static void afterExecute() {
ThreadPoolExecutor executorService = new ThreadPoolExecutor(1, 1, 0, TimeUnit.DAYS,
new LinkedBlockingQueue<>()) {
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t != null) {
logger.error("出现了异常", t);
}
}
};
for (int i = 0; i < 5; i++) {
int finalI = i;
executorService.execute(() -> {
if (finalI == 3) {
throw new RuntimeException("aabb");
}
logger.info("lzc" + finalI);
});
}
}
//测试结果 控制台输出
2020-06-04 12:51:42.070 INFO pool-2-thread-1 com.alioo.pool.ExceptionPoolDemo 35 lzc0
2020-06-04 12:51:42.073 INFO pool-2-thread-1 com.alioo.pool.ExceptionPoolDemo 35 lzc1
2020-06-04 12:51:42.073 INFO pool-2-thread-1 com.alioo.pool.ExceptionPoolDemo 35 lzc2
2020-06-04 12:51:55.549 ERROR pool-2-thread-1 com.alioo.pool.ExceptionPoolDemo 23 出现了异常
java.lang.RuntimeException: aabb
at com.alioo.pool.ExceptionPoolDemo.lambda$afterExecute$0(ExceptionPoolDemo.java:33)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
2020-06-04 12:51:55.552 INFO pool-2-thread-2 com.alioo.pool.ExceptionPoolDemo 35 lzc4
Exception in thread "pool-2-thread-1" java.lang.RuntimeException: aabb
at com.alioo.pool.ExceptionPoolDemo.lambda$afterExecute$0(ExceptionPoolDemo.java:33)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
//测试结果 日志文件输出
2020-06-04 12:51:42.070 INFO pool-2-thread-1 com.alioo.pool.ExceptionPoolDemo 35 lzc0
2020-06-04 12:51:42.073 INFO pool-2-thread-1 com.alioo.pool.ExceptionPoolDemo 35 lzc1
2020-06-04 12:51:42.073 INFO pool-2-thread-1 com.alioo.pool.ExceptionPoolDemo 35 lzc2
2020-06-04 12:51:55.549 ERROR pool-2-thread-1 com.alioo.pool.ExceptionPoolDemo 23 出现了异常
java.lang.RuntimeException: aabb
at com.alioo.pool.ExceptionPoolDemo.lambda$afterExecute$0(ExceptionPoolDemo.java:33)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
2020-06-04 12:51:55.552 INFO pool-2-thread-2 com.alioo.pool.ExceptionPoolDemo 35 lzc4
这里使用了自定义的MyThreadFactory,在MyThreadFactory中创建线程时使用自定义的MyThreadGroup,并且重写了uncaughtException方法,当遇到异常时记录到日志文件里
//测试代码
public static void myThreadGroup() {
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 1, 0, TimeUnit.DAYS,
new LinkedBlockingQueue<>(), new MyThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 5; i++) {
int finalI = i;
pool.execute(() -> {
if (finalI == 3) {
throw new RuntimeException("abcdefg");
}
logger.info("lzc" + finalI);
});
}
}
public class MyThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
public MyThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = new MyThreadGroup("mythreadgroup");
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon()) { t.setDaemon(false); }
if (t.getPriority() != Thread.NORM_PRIORITY) { t.setPriority(Thread.NORM_PRIORITY); }
return t;
}
}
class MyThreadGroup extends ThreadGroup {
private static Logger logger = LoggerFactory.getLogger(MyThreadGroup.class);
public MyThreadGroup(String name) {
super(name);
}
@Override
public void uncaughtException(Thread t, Throwable e) {
//if (parent != null) {
// parent.uncaughtException(t, e);
//} else {
Thread.UncaughtExceptionHandler ueh =
Thread.getDefaultUncaughtExceptionHandler();
if (ueh != null) {
ueh.uncaughtException(t, e);
} else if (!(e instanceof ThreadDeath)) {
logger.error("兄弟,你的业务方法出现异常咋不处理呢,Exception in thread \""
+ t.getName() + "\" ",e);
}
//}
}
}
//测试结果 控制台输出
2020-06-04 13:59:37.517 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 55 lzc0
2020-06-04 13:59:37.522 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 55 lzc1
2020-06-04 13:59:37.522 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 55 lzc2
2020-06-04 13:59:37.618 INFO pool-1-thread-2 com.alioo.pool.ExceptionPoolDemo 55 lzc4
2020-06-04 13:59:37.622 ERROR pool-1-thread-1 com.alioo.pool.MyThreadGroup 52 兄弟,你的业务方法出现异常咋不处理呢,Exception in thread "pool-1-thread-1"
java.lang.RuntimeException: abcdefg
at com.alioo.pool.ExceptionPoolDemo.lambda$uncaughtExceptionHandler$1(ExceptionPoolDemo.java:53)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
//测试结果 日志文件输出
2020-06-04 13:59:37.517 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 55 lzc0
2020-06-04 13:59:37.522 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 55 lzc1
2020-06-04 13:59:37.522 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 55 lzc2
2020-06-04 13:59:37.618 INFO pool-1-thread-2 com.alioo.pool.ExceptionPoolDemo 55 lzc4
2020-06-04 13:59:37.622 ERROR pool-1-thread-1 com.alioo.pool.MyThreadGroup 52 兄弟,你的业务方法出现异常咋不处理呢,Exception in thread "pool-1-thread-1"
java.lang.RuntimeException: abcdefg
at com.alioo.pool.ExceptionPoolDemo.lambda$uncaughtExceptionHandler$1(ExceptionPoolDemo.java:53)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
跟上面的方法类似,都是重写ThreadFactory,上面是在创建线程时指定ThreadGroup,现在是线程按照默认方式创建完了,重置下ThreadGroup,仅此而已。
//测试代码
public static void uncaughtExceptionHandler() {
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 1, 0, TimeUnit.DAYS,
new LinkedBlockingQueue<>(), new MyThreadFactory2(), new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 5; i++) {
int finalI = i;
pool.execute(() -> {
if (finalI == 3) {
throw new RuntimeException("abcdefg");
}
logger.info("lzc" + finalI);
});
}
}
public class MyThreadFactory2 implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
MyThreadFactory2() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
t.setUncaughtExceptionHandler(new MyThreadGroup2("mythreadgroup"));
return t;
}
}
class MyThreadGroup2 extends ThreadGroup {
private static Logger logger = LoggerFactory.getLogger(MyThreadGroup.class);
public MyThreadGroup2(String name) {
super(name);
}
@Override
public void uncaughtException(Thread t, Throwable e) {
//if (parent != null) {
// parent.uncaughtException(t, e);
//} else {
Thread.UncaughtExceptionHandler ueh =
Thread.getDefaultUncaughtExceptionHandler();
if (ueh != null) {
ueh.uncaughtException(t, e);
} else if (!(e instanceof ThreadDeath)) {
logger.error("兄弟,你的业务方法出现异常咋不处理呢,Exception in thread \""
+ t.getName() + "\" ",e);
}
//}
}
}
//测试结果 控制台输出
2020-06-04 14:17:57.439 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 74 lzc0
2020-06-04 14:17:57.443 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 74 lzc1
2020-06-04 14:17:57.443 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 74 lzc2
2020-06-04 14:17:58.047 INFO pool-1-thread-2 com.alioo.pool.ExceptionPoolDemo 74 lzc4
2020-06-04 14:17:58.049 ERROR pool-1-thread-1 com.alioo.pool.MyThreadGroup 55 兄弟,你的业务方法出现异常咋不处理呢,Exception in thread "pool-1-thread-1"
java.lang.RuntimeException: abcdefg
at com.alioo.pool.ExceptionPoolDemo.lambda$uncaughtExceptionHandler$2(ExceptionPoolDemo.java:72)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
//测试结果 日志文件输出
2020-06-04 14:17:57.439 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 74 lzc0
2020-06-04 14:17:57.443 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 74 lzc1
2020-06-04 14:17:57.443 INFO pool-1-thread-1 com.alioo.pool.ExceptionPoolDemo 74 lzc2
2020-06-04 14:17:58.047 INFO pool-1-thread-2 com.alioo.pool.ExceptionPoolDemo 74 lzc4
2020-06-04 14:17:58.049 ERROR pool-1-thread-1 com.alioo.pool.MyThreadGroup 55 兄弟,你的业务方法出现异常咋不处理呢,Exception in thread "pool-1-thread-1"
java.lang.RuntimeException: abcdefg
at com.alioo.pool.ExceptionPoolDemo.lambda$uncaughtExceptionHandler$2(ExceptionPoolDemo.java:72)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
参考文章
https://www.cnblogs.com/Laymen/p/11465881.html