ThreadPool
Java线程池
Java接口
public class ThreadPoolTest {
public static void main(String[] args) {
ExecutorService e1 = Executors.newSingleThreadPool();
ExecutorService e2 = Executors.newCachedThreadPool();
ExecutorService e3 = Executors.newFixedThreadPool();
ExecutorService e4 = Executors.newScheduledThreadPool(10);
}
}
本质上前三个最终都调用了ThreadPoolExecutor,知识传进去的参数不一样
eg
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
参数分别为核心线程数,最大线程数,线程存活时间,时间单位,阻塞队列
基本的流程为,先创建核心线程接受请求,在数量达到最大值后请求进入阻塞队列,阻塞队列如果满了,就创建普通线程执行后续请求,注意是后续请求而不是先执行队列里面的。如果线程总数达到了maximumPoolSize,并且所有线程都在工作中,如果还有新的请求就会抛出异常。
上面大致是最坏情况,一般来说,在队列满之前核心线程或者可能就执行完请求了,那么根据keepAliveTime参数,如果普通线程在这段时间内没有接受到请求,那么这个普通线程就会被GC。注意核心线程不会被回收。
cachedThreadPool
场景:大量执行时间较短(否则容易oom)的任务
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
核心线程数0,总线程数最大值,同步队列大小为0
所以一开始任务进来后因为核心线程已经达到了最大值0,所以要进入队列,但队列的大小也为0,所以会创建普通线程执行任务,知道线程数达到Integer.MAX_VALUE后还有新任务进入,才会抛异常。没事情干的线程60s后被回收。(60s是否合理?线程数量太大会不会OOM?)
fixedThreadPool
场景:任务量已知,相对耗时
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
核心线程数=最大线程数,没事干的普通线程会立刻销毁,但是因为永远不会有普通线程,所以线程的数量是固定的,没有线程会被销毁,此外队列是无限大的。(无穷队列是否会OOM?)
scheduledThreadPool
场景:定时或周期任务
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
假如我们需要三秒后执行某个任务,那么就可以用到scheduledThreadPool
ScheduledExecutorService se1 = Executors.newScheduledThreadPool(2);
se1.schedule(() -> System.out.println("aaa"), 3, TimeUnit.SECONDS);
如果需要周期性的执行某个任务,三秒之后开始执行,每三秒再执行一次
// 保证三秒钟执行一次任务
se1.scheduleAtFixedRate(() -> System.out.println("aaa"), 3, 3, TimeUnit.SECONDS);
// 保证这个任务之间的间隔一定是3秒(任务完成时数三秒再执行任务)
se1.scheduleWithFixedDelay(() -> System.out.println("aaa"), 3, 3, TimeUnit.SECONDS);