1.使用线程池抛出异常处理:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
package com.tomdd.wechatarticle;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.junit.jupiter.api.Test;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
/**
* <h1>线程池中抛出异常测试</h1>
* 线程池中线程频繁出现未捕获异常
* 那线程的复用率就大大降低了,需要不断地创建新线程。
*
* @author zx
* @date 2022年10月17日 11:20
*/
public class ThreadExecutorTest {
/**
* ·corePool:核心线程池的大小。
* ·maximumPool:最大线程池的大小。
* ·BlockingQueue:用来暂时保存任务的工作队列。
* ·RejectedExecutionHandler:当ThreadPoolExecutor已经关闭或ThreadPoolExecutor已经饱和时(达到了最大线程池大小且工作队列已满),execute()方法将要调用的Handler。
*/
//private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
// new ArrayBlockingQueue<>(200), new NameTreadFactory(), new MyIgnorePolicy());
//new ThreadFactoryBuilder() guava 工具类提供的。
//private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
// new ArrayBlockingQueue<>(200), new ThreadFactoryBuilder().setNameFormat("customThread %d").build());
//要捕获那些没被业务代码捕获的异常,可以设置Thread类的uncaughtExceptionHandler属性。
// 这时使用ThreadFactoryBuilder会比较方便,ThreadFactoryBuilder是guava提供的ThreadFactory生成器。
private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(200), new ThreadFactoryBuilder().setNameFormat("customThread %d")
.setUncaughtExceptionHandler((t, e) -> System.out.println(t.getName() + "发生异常" + e.getCause()))
.build());
/**
* 线程池中的线程执行任务抛出异常没有被捕获
* 在不断地创建新线程
*/
@Test
public void test01() {
IntStream.rangeClosed(1, 5).forEach(i -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadPoolExecutor.execute(() -> {
int j = 1 / 0;
});
});
}
/**
* 线程池中执行任务的线程如果抛出异常没有被捕获
* 设置Thread类的uncaughtExceptionHandler属性,线程池中原有的线程没有复用
* 通过UncaughtExceptionHandler的设置,只是做了一层异常的保底处理。
*/
@Test
public void test02() {
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;
});
});
}
/**
* 线程池中执行任务的线程如果抛出异常没有被捕获
* 设置Thread类的uncaughtExceptionHandler属性,并没有实现复用效果
* <p>
* 把excute改成submit试试:
* 通过submit提交线程可以屏蔽线程中产生的异常,达到线程复用。当get()执行结果时异常才会抛出
* 通过submit提交的线程,当发生异常时,会将异常保存,待future.get();时才会抛出
*
* 总结:
* 1、线程池中线程中异常尽量手动捕获
*
* 2、通过设置ThreadFactory的UncaughtExceptionHandler可以对未捕获的异常做保底处理,通过execute提交任务,线程依然会中断,而通过submit提交任务,可以获取线程执行结果,线程异常会在get执行结果时抛出。
*/
@Test
public void test03() {
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();
}
});
}
/**
* 线程池中的线程执行任务抛出异常被捕获
* 线程池中的额线程可以被重复使用
*/
@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());
}
});
});
}
//设置线程名称
static class NameTreadFactory implements ThreadFactory {
private final AtomicInteger mThreadNum = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "my-thread-" + mThreadNum.getAndIncrement());
System.out.println(t.getName() + " has been created");
return t;
}
}
//拒绝策略: 当ThreadPoolExecutor已经关闭或ThreadPoolExecutor已经饱和时(达到了最大线程池大小且工作队列已满)
static class MyIgnorePolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
doLog(r, e);
}
private void doLog(Runnable r, ThreadPoolExecutor e) {
System.err.println(r.toString() + " rejected");
System.out.println("completedTaskCount: " + e.getCompletedTaskCount());
}
}
}
2.总结:
- 线程池中线程中异常尽量手动捕获
- 通过设置
ThreadFactory
的UncaughtExceptionHandler
可以对未捕获的异常做保底处理,通过execute提交任务,线程依然会中断,而通过submit提交任务,可以获取线程执行结果,线程异常会在get执行结果时抛出