通俗的讲,线程池就是一个线程集合,里面已经提前创建好了若干个线程,当需要线程的时候到线程集合里获取一个即可,这样省去了创建线程的时间,当然也省去了GC回收线程的时间,当线程池里的线程都被使用了后,只能阻塞等待了,等待获取线程池后被释放的线程,这样就不会无限制的去创建线程而导致Out of Memory
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 3L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20));
for (int i = 0; i < 20; i++) {
threadPoolExecutor.submit(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
});
}
}
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize:核心线程数,线程池会维护线程的最少数量,默认情况下核心线程会一直存活,即使没有任务也不会受keepAliveTime控制
坑:在刚创建线程池时线程不会立刻启动,到有任务提交时才开始创建线程并逐步线程数目达到corePoolSize
maximumPoolSize:线程池维护线程的最大数量,超过将被阻塞
坑:当核心线程满,且阻塞队列也满时,才会判断当前线程数是否小于最大线程数,才决定是否创建新线程
keepAliveTime:非核心线程的闲置超时时间,超过这个时间就会被回收,直到线程数量等于corePoolSize
unit:指定keepAliveTime的单位,如TimeUnit.SECONDS、TimeUnit.MILLISECONDS
workQueue:线程池中的任务队列,常用的是ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue
threadFactory:创建新线程时使用的工厂
handler:RejectedExecutionHandler是一个接口且只有一个方法,线程池中的数量大于maximumPoolSize,对拒绝任务的处理策略,默认有4种策略:AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy
尽量避免使用Executors创建线程池
Executors创建线程池底层也是调用ThreadPoolExecutor,只不过使用不同的参数、队列、拒绝策略等,如果使用不当,会造成资源耗尽问题;直接使用ThreadPoolExecutor让使用者更加清楚线程池规则、常见参数的使用,避免风险
常见的线程池问题:
newFixedThreadPool和newSingleThreadExecutor:队列使用LinkedBlockingQueue,队列长度为Integer.MAX_VALUE,可能造成堆积导致OOM
newScheduledThreadPool和newCachedThreadPool:线程池里面允许最大的线程数是Integer.MAX_VALUE,可能会创建多线程导致OOM
创建线程池时,核心线程数不要过大
相应的逻辑,发生异常时要处理
submit如果发生异常不会立即抛出,而是在get的时候再抛出异常
execute发生异常直接抛出异常