线程池详解

定义

线程池(ThreadPool)是一种基于池化思想管理和使用线程的机制。它是将多个线程预先存储在一个“池子”内,当有任务出现时可以避免重新创建和销毁线程所带来性能开销,只需要从“池子”内取出相应的线程执行对应的任务即可。常见的运用池化思想的有:内存池、数据库连接池。使用线程池的优点如下:

优点

1.减少频繁创建和销毁线程的性能开销
2.重复利用线程 避免对每个任务都创建线程 可以提高响应速度
3.合理设置线程池的大小 可以避免因为线程池过大影响性能

线程池种类

1.Executors.newFixedThreadPool()

创建一个有固定线程数量的线程池

2.Executors.newWorkStealingPool()

创建一个fork/join的线程池

3.Executors.newSingleThreadExecutor()

创建只有一个线程的线程池 这个线程池只有一个核心线程

4.Executors.newCachedThreadPool()

创建一个可缓存线程的线程池 该线程池不限制线程的数量 它会根据任务数量产生对应数量的线程 并且这些线程创建后会缓存起来 可以重复使用 直到任务数量降低后释放

5.Executors.newScheduledThreadPool()

创建一个有固定线程数量的线程池 并且允许延期执行 以及按照周期反复执行 类似于定时任务机制

6.Executors.newWorkStealingPool

创建一个抢占式执行的线程池(任务执行顺序不确定)JDK 1.8 中添加

自定义线程池

 private BlockingQueue<Runnable> executeQueue = new ArrayBlockingQueue<>(1);
        // 核心线程数
        //最大工作线程数
        //空闲线程的存活时间
        //单位 
         //等待队列
          //工厂
         //拒绝策略
        private ExecutorService executor = new ThreadPoolExecutor(1, 1 * 4, 1,
                TimeUnit.SECONDS, executeQueue, Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

SpringBoot中应用(不建议使用)

@Configuration
@EnableAsync
public class ExecutorConfig {

    private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);

    @Bean
    public Executor asyncServiceExecutor() {
        logger.info("start asyncServiceExecutor");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(5);
        //配置最大线程数
        executor.setMaxPoolSize(5);
        //配置队列大小
        executor.setQueueCapacity(99999);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix("async-service-");

        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }
}
    //注解指定线程池的名字
    @Async("asyncServiceExecutor")
    public void executeAsync() {
        logger.info("start executeAsync");
        try{
            Thread.sleep(1000);
        }catch(Exception e){
            e.printStackTrace();
        }
        logger.info("end executeAsync");
    }

应用案例

public class ThreadPoolTest {
    public static void main(String[] args) {
        ExecutorService executorService = new ThreadPoolExecutor(3,5,1L, TimeUnit.SECONDS,new ArrayBlockingQueue(4),Executors.defaultThreadFactory());
        for(int i=0;i<7;i++){
            executorService.execute(()->{
                System.out.println(Thread.currentThread().getName()+" "+"--->办理业务");
            });
        }
        executorService.shutdown();
    }
}

线程池执行流程

在这里插入图片描述
1.如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
2.如果此时线程池中的数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列。
3.如果此时线程池中的数量大于等于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的工作线程来处理被添加的任务。
4.如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
5.当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

注意点

1.当workQueue使用的是无界限队列时,maximumPoolSize参数就变的无意义了,比如new LinkedBlockingQueue(),或者new ArrayBlockingQueue(Integer.MAX_VALUE);
2.使用SynchronousQueue队列时由于该队列没有容量的特性,所以不会对任务进行排队,如果线程池中没有空闲线程,会立即创建一个新线程来接收这个任务。maximumPoolSize要设置大一点。
3.核心线程和最大线程数量相等时keepAliveTime无作用.

线程池的状态

1.RUNNING

线程池创建就是 RUNNING状态 可以接收任务并对任务进行处理

2.SHUTDOWN

不能接收新任务 但是可以处理 已经添加的任务

3.Stop

(1) 状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
(2) 状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。

4.TIDYING

(1) 状态说明:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
(2) 状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。
当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。

5.TERMINATED

(1) 状态说明:线程池彻底终止,就变成TERMINATED状态。
线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。

源码解析

//高三位表示线程状态 低29位表示线程数量  
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
  //统计数量的位数
   private static final int COUNT_BITS = Integer.SIZE - 3;
   //线程池容量
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        //判断当前线程数量是否小于核心线程数 如果是false 表示已经启动
        if (workerCountOf(c) < corePoolSize) {
            //创建一个线程并启动 同时添加任务 
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
       //如果线程已经启动 通过 offer方法将当前任务加入阻塞队列中
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
             //如果线程池不是运行状态,则将刚添加的任务从队列移除并执行拒绝策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
            //判断当前线程数量,如果线程数量为0,则添加一个非核心线程,并且不指定首次执行任务
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
       //如果阻塞队列已满 添加非核心线程 
        else if (!addWorker(command, false))
            //如果全满了 执行拒绝策略
            reject(command);
    }
private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            //>= SHUTDOWN 表示线程状态是 SHUTDOWN,Stop,TIDYING,TERMINATED
            if (rs >= SHUTDOWN &&
               //当前线程池调用了shutdown()方法但是任务没有执行
               //如果不满足条件表示不需要创建新的线程 直接跳出
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                //判断工作线程是否超过阈值
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    //工作线程已经超过阈值 返回直接拒绝策略
                    return false;
                //没有超过阈值cas 操作完成工作线程的更新
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            //创建一个worker封装线程
            w = new Worker(firstTask);
            //拿到线程
            final Thread t = w.thread;
            if (t != null) {
                //加锁 避免创建工作线程的时候把其他工作线程关闭
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());
                    // rs < SHUTDOWN 表示这是 RUNNING状态
                    if (rs < SHUTDOWN ||
                        //rs == SHUTDOWN 当前没有任务提交过来
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        //将线程假如 set 中
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    //启动线程
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

启动 Worker 会调用 Worker中的run方法

 final void runWorker(Worker w) {
        //获取当前线程
        Thread wt = Thread.currentThread();
        //取出当前要执行的任务
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
              // task!= null 并且该任务是从阻塞队列中获取的
            while (task != null || (task = getTask()) != null) {
                //加锁
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                //判断线程是否中断
                //如果线程处于 STOP状态 并且线程没有中断则调用wt.interrupt()进行中断
                //如果线程处于 stop状态 并且线程已经被中断也调用 wt.interrupt()进行中断
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        //由工作线程来执行任务
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }
private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            //如果线程处于非 RUNNING状态 && 线程处于stop状态或者 阻塞队列==null直接返回null并将Worker数量-1
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

          
           // 注意这里的allowCoreThreadTimeOut参数,字面意思是否允许核心线程超时,即如果我们设置为false,那么只有当线程数wc大于corePoolSize的时候才会超时
           //更直接的意思就是,如果设置allowCoreThreadTimeOut为false,那么线程池在达到corePoolSize个工作线程之前,不会让闲置的工作线程退出
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
              //确认超时,将Worker数-1,然后返回
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                //从阻塞队列取出任务
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

拒绝策略

1.AbortPolicy

默认使用的拒绝策略 直接抛异常

2.CallerRunsPolicy

如果线程池还有没有被执行的方法 就由提交任务的线程通过 run方法直接以普通方法执行任务

3.DiscardPolicy

直接把任务丢弃 不做任何处理 不建议放在重要业务上

4.DiscardOldestPolicy

如果线程池没有调用shutdown方法 则通过 e.getQueue().poll 把队列头部等待最久的任务丢弃 然后把当前任务通过 execute()方法提交到阻塞队列中

5.自定义拒绝策略

阻塞队列

直接提交的队列

SynchronousQueue 没有容量 每一个插入操作都要等待一个相应的删除操作 反之每个删除操作都要等待对应的插入操作 如果使用 SynchronousQueue 提交的任务不会被真实的保存 而总是将新任务提交给线程执行 如果没有空闲的线程 则尝试创建新的线程 如果线程数量已经达到最大值 则执行拒绝策略

有界的任务队列

ArrayBlockingQueue
当使用有界队列的任务时 若有新的任务需要执行 如果线程池的实际线程数小于 核心线程数 则会优先创建新 线程 若大于核心线程数 则会将新任务假如等待队列 若等待队列已满 若大于工作线程数 执行拒绝策略

无界的任务队列

LinkedBlockingQueue
无界的任务队列不存在任务入队失败的情况 当有新的任务到来 系统的线程数小于核心线程数时 线程池会生成新的线程执行任务 当线程数达到核心线程书后 就不会继续增加 若后续仍有新的任务假如 而没有空心啊的线程资源 则任务进入队列等待

优先任务队列

PriorityBlockingQueue
根据自身优先级顺序先后执行

线程池参数的配置

N(cpu) 表示cpu的数量 可以通过Runtime.getRuntime().availableProcessors()获得
U(cpu) 表示期待的CPU的使用率
W/C 表示等待时间与计算时间的比例
N(threads) 表示线程数量的计算公式
N(threads) = N(cpu) *U(cpu) (1+W/C)
假设CPU利用率是100% 那么 N(threads) = N(cpu)
(1+W/C) 也就意味着 W/C的值越大 那么现成数量越多 反之线程数量越少

IO密集型

线程频繁需要和磁盘或者远程网络通信 这种场景中磁盘的耗时和网络通信的耗时较大 意味着线程处于阻塞期间不会占用CPU资源 所以线程数量设置超时CPU核心数并不会造成问题
线程大小设置成N+1 (N表示CPU的核心数量)

CPU密集型

就是对CPU的利用率较高的场景 比如循环递归 逻辑运算等 这种情况下线程数量设置越少 就越能减少CPU的上下文频率切换
线程大小设置成2N+1 (N表示CPU的核心数量)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值