线程池的基本原理和使用

线程是否越多越好

线程在java中是一个对象,更是操作系统的资源,线程的创建、销毁都需要时间。如果创建+销毁时间>线程任务的执行时间,创建该线程就会很不合算。
java对象占用堆内存,操作系统线程占用系统内存,根据jvm规范,一个线程默认最大栈大小为1M,这个栈空间是需要从系统内存中分配的,线程过多,会消耗大量内存。
线程数量过多,争抢cpu时间执行分片,操作系统频繁切换线程上下文,会影响性能。
线程并不是越多越好,因此我们可以通过线程池来节约线程开销,避免线程资源浪费。

线程池的基本概念

核心线程数(corePoolSize):当有新任务提交的时候,线程池首先会检查核心线程的数量,如果核心线程都在工作,并且线程数量也已经到达corePoolSize,就不会再新建线程,而是将任务放到任务队列中,等待执行。
当任务执行完后,线程池还是会保持corePoolSize数量的线程,这些线程不会因为空闲而销毁,除非调用allowCoreThreadTimeOut。

等待队列(workQueue):等待队列用于存储当核心线程都在工作的时候,继续新增的任务,核心线程执行完任务之后,会往等待队列里面拉取新的任务进行执行。这个队列一般是一个线程安全的阻塞队列,它的容量可以由开发者自己指定。

最大线程数(maximumPoolSize):当等待队列满了,而当前的线程数还没有超过最大线程数maximumPoolSize,线程池就会新建线程来执行任务。最多可以新建出来maximumPoolSize减去corePoolSize个线程。

线程活动保持时间(keepAliveTime):当线程池的线程总数量大于corePoolSize时,线程池中的空闲的线程保持存活的最大时间。在这个时间内,如果空闲线程没有被使用,多于corePoolSize的线程就会销毁。

拒绝策略(RejectedExecutionHandler):当等待队列已满,同时线程池线程数量也已经达到maximumPoolSize,线程池将会根据拒绝策略来执行后续新加入的任务。开发者可以自定义拒绝策略,默认策略是直接抛弃任务。在这里插入图片描述

关于ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor用于执行定时任务,继承于ThreadPoolExecutor,实现了ScheduledExecutorService。

public class ScheduledThreadPoolExecutor
        extends ThreadPoolExecutor
        implements ScheduledExecutorService {
   
        //...
}

ScheduledThreadPoolExecutor构造方法的实际是直接调用父类ThreadPoolExecutor的构造函数,通过不同的构造函数可以指定corePoolSize、ThreadFactory、RejectedExecutionHandler。

/*通过父类ThreadPoolExecutor构造出一个指定核心线程数量、最大线程数为Integer.MAX_VALUE,
初始等待队列为16(DelayedWorkQueue的初始容量是16)的线程池*/
public ScheduledThreadPoolExecutor(int corePoolSize) {
   
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
    
public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory) {
   
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), threadFactory);
    }
    
public ScheduledThreadPoolExecutor(int corePoolSize,
                                       RejectedExecutionHandler handler) {
   
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), handler);
    }

ScheduledExecutorService定义了四个任务执行方法:
schedule是在指定延时时间后单次调度任务。
scheduleAtFixedRate和scheduleWithFixedDelay,两者都是指定延迟后,周期性执行任务。
区别是当scheduleAtFixedRate遇到当前任务执行时间超过了指定的延迟时间,下一个任务会在当前任务执行完后直接进入执行;而scheduleWithFixedDelay在当前任务执行完后仍然会等待一个延迟时间,然后再执行下一个任务。

public interface ScheduledExecutorService extends ExecutorService {
   
    //指定延时时间后调度执行任务(单次调度)
    public ScheduledFuture<?> schedule(Runnable command,
                                       long delay, TimeUnit unit);
    //指定延时时间后调度执行任务(单次调度)
    public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                           long delay, TimeUnit unit);
    //指定延时时间后开始任务,之后每隔period时长,再次执行(周期调度)
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);
    //指定延时时间后开始任务,前一个任务执行完后,等待delay时长,再次执行(周期调度)
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);
}

关于scheduleAtFixedRate和scheduleWithFixedDelay是怎么在底层实现不同延时策略的一个简单分析:
在源码里,这两个方法的唯一区别是在new ScheduledFutureTask的时候,对于第四个参数period的传值区别,前者是unit.toNanos(period),后者是unit.toNanos(-delay)。在后续的setNextRunTime方法中,就会根据这个值来设置任务执行时间time。
源码如下:

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit) {
   
        //...
        ScheduledFutureTask<Void> sft =
            new ScheduledFutureTask<Void>(command,
                                          null,
                                          triggerTime(initialDelay, unit),
                                          unit.toNanos(period));
        //...
    }
    
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                           
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值