基础线程池介绍(基于ThreadPoolExecutor类)

目录

CachedThreadPool

FixedThreadPool

SingleThreadExecutor

ScheduledThreadPool

总结


CachedThreadPool

缓存线程池:使用方式

ExecutorService cT= Executors.newCachedThreadPool();

    /**
    创建一个线程池,根据需要创建新线程,但在可用时将重用先前构造的线程。 这些池通常会提高执行许多短期异步任务的程序的性能。 如果可用,调用execute将重用先前构造的线程。 如果没有可用的现有线程,则会创建一个新线程并将其添加到池中。 60 秒内未使用的线程将被终止并从缓存中删除。 因此,保持空闲足够长时间的池不会消耗任何资源。 请注意,可以使用ThreadPoolExecutor构造函数创建具有相似属性但细节不同(例如,超时参数)的ThreadPoolExecutor 。
    返回:
    新创建的线程池
     */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

        可以看出,其线程全为非核心线程,其在空闲时会计算生存时间,这里为60s,生存时间过了就会销毁线程,另外,由于工作队列用的是同步移交队列,即作为一个中介工厂生产队列在这阻塞,直到需要执行新任务时在这拿。这里的问题在于线程的最大线程数过大,如果很多任务都要同时需要执行的时候,都会创建一个新线程。这样因为每个线程都有一个独立的虚拟机栈空间 Xss默认为1M,当有10000个任务时至少需要越10g的内存来创建这些线程,很容易OOM。

FixedThreadPool

固定数目线程池:所有任务只能使用固定大小的线程;

ExecutorService fT = Executors.newFixedThreadPool(10);
    /**
    创建一个线程池,该线程池重用固定数量的线程在共享的无界队列中运行。 在任何时候,最多nThreads线程将是活动的处理任务。 如果在所有线程都处于活动状态时提交了其他任务,它们将在队列中等待,直到有线程可用。 如果任何线程在关闭前的执行过程中因失败而终止,则在需要执行后续任务时,将有一个新线程取而代之。 池中的线程将一直存在,直到它被明确shutdown 。
    参数:
    nThreads – 池中的线程数
    返回:
    新创建的线程池
    抛出:
    IllegalArgumentException – 如果nThreads <= 0
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }


    /**
		创建LinkedBlockingQueue ,容量Integer.MAX_VALUE 。
     */
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

        可以看出,其线程全为核心线程,没有非核心线程,即这里面的线程均为常驻线程(默认不关闭线程池就不会销毁里面的线程)。但是值得注意的是其中的工作队列用的是无界(容量​)的阻塞队列。这存在一个问题,这意味着永远不会走拒绝策略,当同时存在很多任务打到线程池时,会创建很多待执行任务积压在队列里面,也容易造成OOM。

SingleThreadExecutor

 /**
      创建一个 Executor,它使用单个工作线程在无界队列中运行。 (但是请注意,如果这个单线程在关闭之前由于执行失败而终止,如果需要执行后续任务,一个新线程将取代它。)保证任务按顺序执行,并且不会超过一个任务处于活动状态在任何给定的时间。 与其他等效的newFixedThreadPool(1) ,返回的执行器保证不可重新配置以使用其他线程。
      返回:
      新创建的单线程 Executor
     */
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

        FixedThreadPool的特殊版本,即只能创建一个新的线程,其余的待执行任务s都会积压在队列里面。

ScheduledThreadPool

周期性线程池:定时以及周期地执行任务。

/**
      创建一个线程池,可以安排命令在给定延迟后运行,或定期执行。
      参数:
      corePoolSize – 要保留在池中的线程数,即使它们处于空闲状态
      返回:
      新创建的调度线程池
      抛出:
      IllegalArgumentException – 如果corePoolSize < 0
     */
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }


    /**
      使用给定的核心池大小创建一个新的ScheduledThreadPoolExecutor 。
      参数:
      corePoolSize – 要保留在池中的线​​程数,即使它们处于空闲状态,除非设置了allowCoreThreadTimeOut
      抛出:
      IllegalArgumentException – 如果corePoolSize < 0
     */
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }


    /**
    池线程的默认保持活动时间。 通常,此值未使用,因为所有池线程都将是核心线程,但是如果用户创建一个 corePoolSize 为零的池(违反我们的建议),只要有排队的任务,我们就会保持线程处于活动状态。 如果保持活动时间为零(历史值),我们最终会在 getTask 中进行热旋转,浪费 CPU。 但另一方面,如果我们将该值设置得太高,并且用户创建了一个他们没有完全关闭的一次性池,则池的非守护线程将阻止 JVM 终止。 一个小的但非零的值(相对于 JVM 的生命周期)似乎是最好的。
     */
    private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;

        这里面的工作队列使用的是延时队列DelayedWorkQueue,总的来说,ScheduledThreadPool能做这样一些事情:

  1. 延时执行任务,可以指定任务提交给线程池之后延迟多久才执行.        
pool.schedule(()->{
            System.out.println("延时5s");
        },5,TimeUnit.SECONDS);
  1. 周期执行任务,希望它能周期反复执行某项任务。
      AtomicInteger count= new AtomicInteger();
        pool.scheduleAtFixedRate(()->{
            System.out.println(count.incrementAndGet());
        },2,1,TimeUnit.SECONDS); //初始延迟时间为2s,然后周期反复每隔1s执行

总结

        可以看出这些在Executors类中提供的便捷线程池对象都是基于ThreadPoolExecutor的,《阿里巴巴开发手册》说过,不建议我们直接这样使用,因为线程池的参数设置都需要看具体环境,一般不设置无界队列和Integer.MAX_VALUE这样的线程数。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值