Java线程池攻略<二>:四种常用线程池剖析

在这里插入图片描述

前言


Java线程池攻略<一>:初识ThreadPoolExecutor
Java线程池攻略<二>:四种常用线程池剖析

基于ThreadPoolExecutor共有四种类型线程池

  • FixedThreadPool:固定数量线程池
  • SingleThreadExecutor:单线程线程池
  • CachedThreadPool:缓存线程池
  • ScheduledThreadPoolExecutor:调度线程池

image.png

FixedThreadPool


固定数量线程池:newFixedThreadPool()

    //被称为可重用固定线程数的线程
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

示例

    public class DemoFixedThreadPool {

        public static void main(String[] args) {

            ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);

            // 创建需执行的任务
            Runnable task = () -> {
                LocalDateTime date=LocalDateTime.now();
                System.out.println(" 当前时间为:"+ date);
            };

            // 线程池添加任务
            fixedThreadPool.execute(task);

            //关闭线程池
            fixedThreadPool.shutdown();

            try {
                fixedThreadPool.awaitTermination(10000, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

特性

  1. 如果当前运行的线程数小于 corePoolSize, 如果再来新任务的话,就创建新的线程来执行任务;
  2. 当前运行的线程数等于 corePoolSize 后, 如果再来新任务的话,会将任务加入 LinkedBlockingQueue
  3. 线程池中的线程执行完 手头的任务后,会在循环中反复从 LinkedBlockingQueue 中获取任务来执行

为什么不推荐使用FixedThreadPool

FixedThreadPool 使用无界队列 LinkedBlockingQueue(队列的容量为 Integer.MAX_VALUE)作为线程池的工作队列会对线程池带来如下影响 :

  1. 当线程池中的线程数达到 corePoolSize 后,新任务将在无界队列中等待,因此线程池中的线程数不会超过 corePoolSize
  2. 由于使用无界队列时 maximumPoolSize 将是一个无效参数,因为不可能存在任务队列满的情况。所以,

通过创建 FixedThreadPool的源码可以看出创建的 FixedThreadPoolcorePoolSizemaximumPoolSize 被设置为同一个值。

  1. 由于 1 和 2,使用无界队列时 keepAliveTime 将是一个无效参数;
  2. 运行中的 FixedThreadPool(未执行 shutdown()或 shutdownNow())不会拒绝任务,在任务比较多的时候会导致 OOM(内存溢出)。

SingleThreadExecutor


单线程线程池:newSingleThreadExecutor()

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

示例

    public class DemoSingleThreadPool {

        public static void main(String[] args) {
            ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

            // 创建需执行的任务
            Runnable task = () -> {
                LocalDateTime date=LocalDateTime.now();
                System.out.println(" 当前时间为:"+ date);
            };

            // 线程池添加任务
            singleThreadExecutor.execute(task);

            //关闭线程池
            singleThreadExecutor.shutdown();

            try {
                singleThreadExecutor.awaitTermination(10000, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

CachedThreadPool

缓存线程池: newCachedThreadPool()

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

示例

    public class DemoCacheThreadPool {

        public static void main(String[] args) {
            ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

            // 创建需执行的任务
            Runnable task = () -> {
                LocalDateTime date=LocalDateTime.now();
                System.out.println(" 当前时间为:"+ date);
            };

            // 线程池添加任务
            cachedThreadPool.execute(task);

            //关闭线程池
            cachedThreadPool.shutdown();

            try {
                cachedThreadPool.awaitTermination(10000, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

特性

CachedThreadPoolcorePoolSize 被设置为空(0),maximumPoolSize被设置为 Integer.MAX.VALUE,空闲线程的等待时间为60秒

即它是无界的,这也就意味着如果主线程提交任务的速度高于 maximumPool 中线程处理任务的速度时,

CachedThreadPool 会不断创建新的线程。极端情况下,这样会导致耗尽 cpu 和内存资源

ScheduledThreadPoolExecutor

调度线程池:newScheduledThreadPool()

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

示例

    public class DemoScheduleThreadPool {

        public static void main(String[] args) {
            ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);

            // 创建需执行的任务
            Runnable task = () -> {
                LocalDateTime date=LocalDateTime.now();
                System.out.println(" 当前时间为:"+ date);
            };

            // 线程池添加任务  每4秒执行一次
            scheduledExecutorService.scheduleAtFixedRate(task, 5L, 4L, TimeUnit.SECONDS);

            //关闭线程池, 不能关闭
            //scheduledExecutorService.shutdown();
        }

    }

特性

image.png

线程 1 从 DelayQueue 中获取已到期的 ScheduledFutureTask(DelayQueue.take())。到期任务是指 ScheduledFutureTask的 time 大于等于当前系统的时间;

  1. 线程 1 执行这个 ScheduledFutureTask;
  2. 线程 1 修改 ScheduledFutureTask 的 time 变量为下次将要被执行的时间;
  3. 线程 1 把这个修改 time 之后的 ScheduledFutureTask 放回 DelayQueue 中(DelayQueue.add())。

线程池参数确定

有一个简单并且适用面比较广的公式:

  • CPU 密集型任务(N+1): 这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU 核心数)+1,比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。
  • I/O 密集型任务(2N): 这种任务应用起来,系统会用大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N。

如何判断是 CPU 密集任务还是 IO 密集任务?

CPU 密集型简单理解就是利用 CPU 计算能力的任务比如你在内存中对大量数据进行排序。但凡涉及到网络读取,文件读取这类都是 IO 密集型,这类任务的特点是 CPU 计算耗费时间相比于等待 IO 操作完成的时间来说很少,大部分时间都花在了等待 IO 操作完成上。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杭州水果捞|Java毕业设计成品

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值