Java多线程并发编程学习笔记【线程池笔记】

1、ThreadPoolExecutor

ThreadPoolExecutor运行状态有五种:

  • RUNNING: 接受新的任务,并处理队列任务。(Accept new tasks and process queued tasks )
  • SHUTDOWN: 不接受新的任务,但是会处理队列中的任务。(Don't accept new tasks, but process queued tasks )
  • STOP: 不接受新的任务,不处理队列任务,并且会中断正在运行的任务。 (Don't accept new tasks, don't process queued tasks,and interrupt in-progress tasks )
  • TIDYING:当所有任务已终止,workerCount为零的时候,运行状态会变为TIDYING,继而运行terminated() 方法。(All tasks have terminated, workerCount is zero,the thread transitioning to state TIDYING will run the terminated() hook method)
  • TERMINATED: terminated() 方法已调用完成。(terminated() has completed)

上面是java源码上的注释,再来看一下运行状态是如何转换的。

RUNNING -> SHUTDOWN:需要调用shutdown()方法,或者调用finalize(),finalize里面会调用shutdown()
(RUNNING or SHUTDOWN) -> STOP:调用shutdownNow()
SHUTDOWN -> TIDYING:线程池为空、任务队列为空的时候,自动转换状态
STOP -> TIDYING:线程池为空,shutdownNow()把任务都干掉了,线程池清空后自动转换
TIDYING -> TERMINATED:terminated()方法完成后。线程池为空、任务队列为空,TIDYING状态确保两者都为空后,线程池被设置终止状态。

如下图

当调用shutdown()方法时,线程池不再接受新任务,但会把原有任务执行完毕。boolean awaitTermination(long timeout, TimeUnit unit)方法可以监听任务执行完毕,使用方法如下

        //等待线程池内任务执行完毕
threadPoolExecutor.shutdown();
//1秒检查一次
        while(!threadPoolExecutor.awaitTermination(1,TimeUnit.SECONDS)) {

        }

或者使用FutureTask的get方法等待结果返回。不过这两种方法都很局限,awaitTermination需要全部线程执行完毕,FutureTask需要任务有返回值。为解决等待子任务执行后再去操作的问题,JDK封装了同步器让开发者更好地使用。

2、类结构图

3、ThreadPoolExecutor构造参数

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) ;

corePoolSize :线程池核心线程个数。初始化时不创建,有任务才创建,后面会保持核心线程运行。

maximunPoolSize : 线程池最大线程数量。maximunPoolSize-corePoolSize=非核心线程数,非核心线程会在空闲的keeyAliveTime时间后被回收。

keeyAliveTime :存活时间。如果当前线程池中的线程数量比核心线程数量多,并且是闲置状态, 则这些闲置的线程能存活的最大时间。

TimeUnit : 存活时间的时间单位。

workQueue :用于保存等待执行的任务的阻塞队列,比如基于数组的有界ArrayBlock ingQueue 、基于链表的无界LinkedBlockingQueue 、最多只有一个元素的同步队列SynchronousQueue 及优先级队列PriorityB lockingQueue 等。

ThreadFactory :创建线程的工厂。

RejectedExecutionHandler :饱和策略, 当队列满并且线程个数达到maximunPoolSize后采取的策略, 比如AbortPolicy (默认策略,抛出异常〉、CallerRunsPolicy (使用调用者所在线程来运行任务) 、DiscardOldestPolicy (调用poll 丢弃一个任务,执行当前任务)及DiscardPolicy (默默丢弃,不抛出异常〉。这四个策略都是ThreadPoolExecutor的内部类。

注意:假如corePoolSize为10,maximunPoolSize为20(20-10=10个非核心线程),workQueue是长度为15的LinkedBlockingQueue。这时有50个任务进来,首先核心线程会处理10个,15个会塞满等待队列,接下来才会开辟新的线程(非核心线程)处理10个。若所有线程都满且还在处理,接下来的任务会触发线程池饱和策略。这里要注意的是,由于非核心线程的存在,使得中间加入的任务可能会比后面加入的更加晚执行。

4、线程池类型

1)newFixedThreadPool :创建一个核心线程个数和最大线程个数都为nThreads 的线程池,并且阻塞队列长度为Integer.MAX_VALUE。keepAliveTIme=0说明线程个数比核心线程个数多并且空闲时,则立马回收多出的线程。

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

public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

这里使用的是LinkedBlockingQueue无参构造函数,队列最大长度为Integer. MAX_VA LUE(2^31-1),可能会导致OOM。

2)newCacheThreadPool :创建一个按需创建线程的线程池,初始线程个数为0,最多线程个数为Integer. MAX_VA LUE ,并且阻塞队列为同步队列。keepAliveTIme=60说明只要当前线程在60s内空闲则回收。这个类型的特殊之处在于, 加入同步队列的任务会被马上执行,同步队列里面最多只有一个任务。

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

由于线程个数最大为Integer. MAX_VA LUE(2^31-1),可能会导致线程过多,cpu100%,也会导致OOM。

3)newSingleThreadExecutor : 创建一个核心线程个数和最大线程个数都为1 的线程池,并且阻塞队列长度为Integer.MAX_VALUE 。keepAliveTIme=0说明只要线程个数比核心线程个数多并且当前空闲则回收。

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

这里使用的是LinkedBlockingQueue无参构造函数,队列最大长度为Integer. MAX_VA LUE(2^31-1),可能会导致OOM。

4)比较好的线程池开启方法

最好不要直接上面三个juc包下Executors类自带的线程池类

ExecutorService executor =  new ThreadPoolExecutor(2,2,
                0,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(2),
                new UserThreadFactory("groupName"));
/**
 * 线程工厂
 * @author :GG
 * @date :Created in 2021/7/3 21:57
 */
public class UserThreadFactory implements ThreadFactory {
        private final String namePrefix;
        private final AtomicInteger nextId = new AtomicInteger(1);
        // 定义线程组名称,在jstack问题排查时,非常有帮助
        public UserThreadFactory(String whatFeaturOfGroup) {
            namePrefix = "From UserThreadFactory's " + whatFeaturOfGroup + "-Worker-";
        }
        @Override
        public Thread newThread(Runnable task) {
            String name = namePrefix + nextId.getAndIncrement();
            Thread thread = new Thread(null, task, name, 0);
            System.out.println(thread.getName());
            return thread;
        }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值