Java基础之多线程篇(三)

2019-11-28  本文已影响0人  youzhihua

一、使用线程池的优点

二、线程池相关体系结构

线程池相关类继承关系.png

三、ThreadPoolExecutor

ThreadPoolExecutor的构造方法有很多,这里我们挑选一个构造参数最多的。

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

corePoolSize:核心线程数线程数定义了最小可以同时运行的线程数量。
maximumPoolSize:线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize。
keepAliveTime:当线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime才会被回收销毁;
unit:keepAliveTime 参数的时间单位。
workQueue:当线程数大于核心线程数时,会进入该队列中。
threadFactory:创建线程使用的工厂。
handler:当线程池内的线程数超过maximumPoolSize时,所进行的拒绝策略,总共有四种定义好的策略。分别为:DiscardOldestPolicy,AbortPolicy,DiscardPolicy,CallerRunsPolicy。

3.1常见线程池

3.1.1 FixedThreadPool

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

3.1.2 SingleThreadExecutor

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

1.如果当前运行的线程数少于 corePoolSize,则创建一个新的线程执行任务;
2.当前线程池中有一个运行的线程后,将任务加入 LinkedBlockingQueue
3.线程执行完当前的任务后,会在循环中反复从 LinkedBlockingQueue 中获取任务来执行;

与FixedThreadPool基本相同,只不过只能同时有一个线程工作。

与FixedThreadPool相同,等待队列是一个无界队列,所以maximumPoolSize和keepAliveTime是无法生效的,若提交任务的速度大于CPU处理任务的速度,便会造成OOM。

3.1.2 CachedThreadPool

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

1.首先执行 SynchronousQueue.offer(Runnable task) 提交任务到任务队列。如果当前 线程池中有闲线程正在执行 SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS),那么主线程执行 offer 操作与空闲线程执行的 poll 操作配对成功,主线程把任务交给空闲线程执行,execute()方法执行完成。
2.当初始线程池为空,或者线程池中没有空闲线程时,将没有线程执行 SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。这种情况下,步骤 1 将失败,此时 CachedThreadPool 会创建新线程执行任务,execute 方法执行完成;

由于线程池的maximumPoolSize是Integer.MAX_VALUE,导致线程池近乎无界,若任务提交过快,也会造成和FixedThreadPool、SingleThreadExecutor一样的OOM。

3.1.4 ScheduledThreadPoolExecutor

主要用来启动定时任务,具体详情可以查看我的另一篇文章。SpringBoot基础教程(十) | 定时器篇

四、线程池示例

4.1 线程池+runnable

/**
 * threadPoolExecutor + runnable
 */
public class ThreadPoolDemo1 {

    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,
                20,
                1L, 
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(10),
                new ThreadPoolExecutor.CallerRunsPolicy());
        for (int i = 0; i < 20; i++) {
            threadPoolExecutor.execute(new RunnaleDemo());
        }
        threadPoolExecutor.shutdown();
        while(!threadPoolExecutor.isTerminated()){
            System.out.println("正在终止");
        }
        System.out.println("终止完成");
    }
}

4.2 线程池+callable


/**
 *  ThreadPoolExecutor+callable
 */
public class ThreadPoolDemo2 {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,
                15,
                1L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(100));

        List<Future<Integer>> futureTasks = new ArrayList<>();

        for (int i = 0; i < 20; i++) {
            futureTasks.add(threadPoolExecutor.submit(new CallableDemo()));
        }

        for (Future future:futureTasks) {
            System.out.println(future.get());
        }

        threadPoolExecutor.shutdown();
    }
}

五、线程池大小的制定规则

上一篇 下一篇

猜你喜欢

热点阅读