1.问题:
在使用juc包的ThreadPoolExecutor创建线程池时候,可以选择不同类型的创建方法,使用Executors提供的newFixedThreadPool()方法时候,源码是这样的:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
使用的是无界队列,如果并发任务量巨大,任务逻辑比较耗时,未来得及处理的任务会大量堆积在队列里,导致内存急速飙高,可能导致程序挂掉。
2.解决方法
线程任务使用有界阻塞队列,队列满了就暂停放入线程任务;但是这样拒绝策略要设置好,因为默认的是超过队列长度就丢弃的策略AbortPolicy,这样不行,应该使用CallerRunsPolicy策略,直接让调用者执行任务。
示例:
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ExecutorService executor = new ThreadPoolExecutor(5, 20, 0,
TimeUnit.SECONDS,
//1.如果使用无界队列,大量提交任务会堆积,造成内存占用太高
// new LinkedBlockingQueue<Runnable>(),
//2.应该使用有界队列
new LinkedBlockingQueue<Runnable>(50),
//2.加上调用线程任务本身去run
new ThreadPoolExecutor.CallerRunsPolicy()
);
AtomicInteger count = new AtomicInteger();
for (int i = 0; i < 100; i++) {
executor.execute(() -> {
// int andIncrement = count.getAndIncrement();
System.out.println("num=" + count.incrementAndGet());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}
以上线程池,使用第2种有界队列的话,长度设为50,并且最后一个构造参数不写,默认就是AbortPolicy,会造成部分任务被丢弃,抛出异常:
java.util.concurrent.RejectedExecutionException: Task cn.demo.configClient.ThreadPoolExecutorTest$$Lambda$1/396180261@5910e440 rejected from java.util.concurrent.ThreadPoolExecutor@6267c3bb[Running, pool size = 20, active threads = 20, queued tasks = 50, completed tasks = 0]
num=9
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)