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);
posted @ 2022-12-07 21:32  antidogmatist  阅读(28)  评论(0编辑  收藏  举报