深入浅析多线程—线程池

一、ThreadPoolExecutor

线程池状态

  • ThreadPoolExecutor 使用 int 高 3 位来表示线程池状态,低 29 位表示线程数量;

  • TERMINATED_011线程池处于终结状态;TIDYING_010任务全部执行完毕,活动线程数为0,即将进入中介状态;STOP_001不再接受新任务__不再处理阻塞队列中的任务 _ 中断正在执行的任务,并且抛弃阻塞队列中的任务;SHUTDOWN_000 不再接受新的任务,但会处理阻塞队列中的剩余任务;RUNNING_111接收新任务,并处理阻塞队列中的任务;

  • ThreadPoolExecutor将两个信息存在一个原子变量ctl中,目的是将线程池状态与线程个数合二为一,这样就可以仅使用一次CAS原子操作为其进行赋值;

    /// c 为旧值,ctlOf 返回结果为新值
    ctl.compareAndSet(c , ctlOf(targeState , workerCountOf(c)));
    ​
    /// rs 为高3位,代表线程池状态, wc 为低29位 代表线程个数, ctl()用于将其合并
    private static int ctlOf(int rs , int wc) { return rs | wc ; }

构造方法

  • public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                            long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
  • corePoolSIze :核心线程数

  • maximumPoolSize:最大线程数

  • keepAliveTime:生存时间 — 针对救急线程

  • unit :时间单位(指定设置生存时间的单位)

  • workQueue:阻塞队列

  • threadFactory:线程工厂 — 可为创建的每个线程起名字(便于分别)

  • handler:拒绝策略

过程描述

  • 若在创建线程时,最大线程数大于核心线程数(则表明除核心线程以外还存在救急线程),当工作的线程数没达到核心线程数的上限时,每当有新任务,线程池便会启动一个核心线程处理任务;

  • 当所有核心线程都在工作时,再有新任务,便会先查看阻塞队列中是否有空间,若有,则将新添加的任务加入阻塞队列中等待;

  • 当阻塞队列已经没有空间时(采取的是有界队列),若再有新任务,但所有核心线程都正在工作,检查的值阻塞队列已满,则会查看是否可产生救急线程(即检查所有工作线程数是否等于参数指定的最大线程数),若允许,线程池创建一个救急线程用于处理该任务(救急线程的空闲存活时间由参数keepAliveTime指定,若救急线程空闲时间达到参数指定的上限,则会被销毁,当再次需要时重新创建);

  • 若阻塞队列已满,正在工作线程数目已达到参数所指定的最大线程数时,再有新任务添加,则根据参数指定拒绝策略进行处理;

拒绝策略

  • 若线程池所有的资源都满负荷运作,再有新任务被提交给线程池时,会执行参数指定的依据参数指定的拒绝策略对该任务进行处理;

  • jdk提供了4种拒绝策略的实现:

    • AbortPlicy :让调用者抛出RejectedExecutionException异常(默认策略);

    • CallerRunsPolicy:让调用者线程自己处理新任务;

    • DiscardPolicy:放弃该任务;

    • DiscardOldestPolicy:放弃队列中最早的任务,用新任务替代其原位置;

  • 其他框架也提供了相关实现:

    • Dubbo:基于抛出RejectedExecutionException异常做了增强,在抛出异常前会记录日志,并dump线程栈信息,方便定位问题;

    • pinPoint:使用了一个拒绝策略链,会逐一尝试决绝策略链中每种决绝策略;

二、newFixedThreadPool

  • 创建固定线程数的线程池:

    public static ExecutorService newFixedThreadPool(int nThreads){
        return new ThreadPoolExecutor(nThreads , nThreads ,
                                      0L , TimeUnit.MILLISECONDS ,
                                      new LinkedBlockingQueue<Runnable>());
    }
    ​
    /// 创建线程池:
    ExecutorService pool = Executors.newFixedThreadPool(2);
    /// 给线程池中提交任务:
    pool.executor(()->{
        /// 任务....
    }); 
    ​
    pool.executor(()->{
        /// 任务....
    });
  • 特点:

    • 核心线程数 == 最大线程数(无救急线程),因此也无须超时时间;

    • 阻塞队列是无界的,可以放任意数量的任务,因此也无须拒绝策略;

  • 适用于已知任务量,相对耗时的任务

三、newCachedThreadPool

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

    • 核心线程数为 0(即没有核心线程,全部都是救急线程),最大线程数是 Integer.MAX_VALUE,救急线程的空闲生存时间是60s(由参数指定);

    • 队列采用了SynchronousQueue其实现特点是,无容量,没有线程来领任务时,无法添加新任务;

  • 整个线程池的表现为:① 线程数会根据任务量不断增长,没有上限,当某个线程执行完任务,空闲时间1min内若无新任务,则自动销毁;② 适合任务数量比较密集,但每个任务执行时间较短的情况

四、newSingleThreadExecutor

  • public static ExecutorService newSingleThreadExecutor(){
        return new FinalizableDelegatedExecutorService(
            new ThreadPoolExecutor(1 , 1 ,
                                   0L , TimeUnit.MILLISECONDS ,
                                   new LinkedBlockingQueue<Runnable>()));
    }
    ​
    // 使用的是无界队列
  • 使用场景:希望多个任务排队执行;线程数固定为 1 ,任务数多于 1 时,会被放入无界队列;任务执行完毕,该唯一的线程也不会被销毁;

  • 区别:

    • 自己创建一个线程串行执行任务,若任务执行失败而告终那么没有任何补救措施,而线程池还会创建新的线程,保证线程池中始终有一个线程能够正常处理任务;

    • Executors.newSingleThreadExecutor()线程个数始终为 1 ,不能修改;

      • FinalizableDelegatedExecutorService应用的是装饰器模式,只对外暴露了ExecutorService接口,因此不能调用ThreadPoolExecutor中特有的方法;

    • Executors.newFixedThreadPool(1)初识时为 1 ,以后还可以进行线程数的修改;

      • 对外暴露的是ThreadPoolExecutor对象,可以强转后调用setCorePoolSize等方法进行修改;

五、提交任务

// 执行任务
void execute(Runnable  r);
​
// 提交任务task,通过返回值Future接收
<T> Future<T> submit(Callable<T> task);
​
// 提交tasks中所有任务
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
​
// 提交tasks中所有任务,带超时时间
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                              long timeout , TimeUnit unit) 
    throws InterruptedException;
​
// 提交tasks中所有的任务,哪个任务先成功执行完毕,返回此任务执行结果,其他任务取消
<T> T invokeAny(Collection<? extends Callable<T>> tasks) 
        throws InterruptedException,ExecutionException;
​
// 提交 tasks 中所有任务,那个任务先成功执行完毕,返回此任务执行结果,其他任务取消,带超时时间
<T> T invokeAny(Collection<? extends Callable<T>> tasks ,
                long timeout , TimeUnit unit) 
        throws InterruptedException,ExecutionException;

六、关闭线程池

shutdown

  • 线程池状态变为:SHUTDOWN

    • 不会再接收新的任务;

    • 已经提交的任务会执行完;

    • 此方法不会阻塞调用线程的执行;

    void shutdown();
    // -----------------------------------
    public void shutdown(){
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try{
            checkShutdownAccess();
            // 修改线程池状态
            advanceRunState(SHUTDOWN);
            // 仅会打断空闲线程
            interruptedIdleWorkers();
            onShutdown();               // 扩展点:ScheduledThreadPoolExecutor
        }finally{
            mainLock.unlock();
        }
        
        // 尝试终结(没有运行的线程可以立刻终结,如果还有运行的线程也不会等)
        tryTerminated();
    }

shutdownNow

  • 线程池状态变为:STOP

    • 不会接收新任务;

    • 会将任务队列中的任务返回;

    • 并用interrupt的方式中断正在执行的任务;

List<Runnable> shutdownNow();
// -----------------------------------------------
public List<Runnable> shutdownNow()}{
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try{
        checkShutdownAccess();
        
        // 修改线程池状态
        advanceRunState(STOP);
        
        // 打断所有线程
        interruptWorkers();
        
        // 获取队列中剩余任务
        tasks = drainQueue();
    }finally {
        mainLock.unlock();
    }
    // 尝试终结
    tryTerminate();
}
  • ####

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值