Java线程池工作流程

本文详细解析了Java线程池的工作流程,包括线程池的创建、任务执行、线程创建与回收、任务缓冲队列等关键环节,并通过示例代码展示了线程池的使用。同时,讨论了线程池中线程执行任务的策略,如核心线程数、最大线程数、任务队列和拒绝策略。最后,提供了线程池的常用API及其应用实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文分析下线程的工作流程

  • 线程池创建。
  • 执行任务过程。
  • 创建线程过程。
  • 线程池中的线程执行任务过程。
  • 回收线程过程。
  • 任务缓冲队列。

因为在工作中遇到了一些线程池的使用问题,百度了许久,都未找到答案,所以就自己去看了下源码。特此记录下。

使用线程池遇到的问题

  • 核心线程已用完,不扩建线程。(最大线程数 > 核心线程数)的情况。

之前的错误理解

  • 核心线程都在工作,再添加任务,无论任务队列是否满了,只要工作线程数小于最大线程数,都会创建线程。
  • 任务放置队列中,是工作线程数达到最大线程数。
  • 核心线程不会被回收。
  • 有独立线程在回收线程池中的空闲线程。

1.0 创建线程池

创建线程池的构造参数。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {}
ThreadPoolExecutor executor = new ThreadPoolExecutor(
                1,                          // 核心线程数,长期保持活跃数量, 不会被回收
                10,                     // 最大线程数, 包括(核心线程数)
                0L,                       // 空闲线程最大允许多长时间被回收
                TimeUnit.MILLISECONDS,                 // keepAliveTime 的时间单位
                new LinkedBlockingQueue<>(2),           // 线程池执行任务队列
                Executors.defaultThreadFactory(),      // 创建线程的工厂对象
                new ThreadPoolExecutor.AbortPolicy()   // 执行任务失败处理器
        );

1.1 threadFactory 构造参数解释

ThreadFactory接口,其中只有一个 newThread()方法,用于创建线程

默认值:Executors.defaultThreadFactory()

代码如下:

  • 设置保护线程为false
  • 设置线程名:pool-poolNumber-thread-threadNumber
  • 设置优先级
DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }

1.3 handler 构造参数解释

RejectedExecutionHandler接口,其中只有一个 rejectedExecution()方法,用于失败执行。

默认值:new ThreadPoolExecutor.AbortPolicy()

代码如下:

失败抛出异常:RejectedExecutionException

public static class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an {@code AbortPolicy}.
         */
        public AbortPolicy() { }

        /**
         * Always throws RejectedExecutionException.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         * @throws RejectedExecutionException always
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }

还可参考其他选项:

  • ThreadPoolExecutor.CallerRunsPolicy 直接执行任务。
  • ThreadPoolExecutor.DiscardPolicy 无任何操作,进行忽略。也就是该任务被丢弃。
  • ThreadPoolExecutor.DiscardOldestPolicy 丢弃队列中的第一个任务,然后重新执行。

2.0 执行任务过程

线程池执行任务:executor.execute(Runnable)

execute 流程
在这里插入图片描述

3.0 创建线程过程

创建线程方法:addWorker(Runnable firstTask, boolean core),core参数表示:是否在小于核心线程数的情况下创建线程。

在这里插入图片描述

4.0 线程池中的线程执行任务过程

线程轮询在队列中获取任务。

5.0 回收线程过程

从任务队列中获取一个任务超时,正常结束。

6.0 任务缓冲队列

核心方法:

  • boolean offer(E) 往队列中添加一个元素。如果队列已满,添加失败,返回fasle。
  • E poll(timeout, unit) 获取并移除队列头节点。如果队列为空,则等待${timeout}时间,如果还是没有数据,则返回null
  • E take() 获取并移除队列头节点。如果队列为空,则一直等待。

7.0 线程池中的线程执行任务总体工作流程

结合以上知识:参考如下图

在这里插入图片描述
大致流程描述:

  • 执行任务,核心线程数未达到corePoolSize该值,就创建线程。否则塞入任务队列。
  • 任务队列满了,就创建线程,但是总线程数不能超过该值maximumPoolSize
  • 如果任务队列满了,线程数达到maximumPoolSize值,则执行失败策略
  • 工作线程则不停的轮询去队列中poll任务,如果poll为空,则工作线程执行结束(回收线程)。
  • 如果工作线程数<=核心线程数corePoolSize,则使用take从队列中获取任务(核心线程一直await)。

8.0 线程池其他常用API

  • prestartCoreThread:预先启动一个核心线程。
  • prestartAllCoreThreads:预先启动全部核心线程。
  • allowCoreThreadTimeOut:设置核心线程是否可以被回收。
  • getActiveCount:获取当前正在执行任务的线程的大致数量。
  • getLargestPoolSize:获取线程池有史以来最大的线程数。
  • getPoolSize:获取当前线程池数量。
  • getCompletedTaskCount:获取已完成执行的任务的大致总数。
  • getQueue:获取任务队列。
  • remove:从任务队列中移除一个任务。
  • shutdown:将队列中的任务执行完毕后,停止线程池执行器。但是不接受新的任务。
  • shutdownNow:尝试停止所有正在执行的任务,暂停正在等待的任务的处理,并返回正在等待执行的任务列表。从该方法返回后,这些任务将从任务队列中排出(删除)。
  • execute:执行一个任务。
  • submit:执行一个任务,可接受Callable类型。

9.0 线程池演示Demo

演示代码如下:

核心线程数2个,最大线程数10个,队列容量20个。
执行 22 次任务。任务里面休眠(1000 * 1000)毫秒。
执行结果:2个核心线程创建,执行两次任务,进入休眠。剩余20次任务都塞入队列。
并不会去创建线程

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo {

    public void test() {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2,          // 核心线程数
                10,     // 最大线程数
                0L,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(20)   // 任务队列容量
        );
        // 执行 22 次任务
        for (int i = 0; i < 22; i++) {
            executor.execute(new DemoRunnable(i));
        }
    }

    public static class DemoRunnable implements Runnable {
        final int value;
        static long time = 1000L * 1000L;   // 休眠长一点
        public DemoRunnable(int value) {
            this.value = value;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " - value = " + value);
            try {
                Thread.sleep(time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        new ThreadPoolDemo().test();
    }

}

执行效果截图:

在这里插入图片描述

将执行任务次设置为23次,队列满了,再执行任务,则会创建线程,进行执行当前任务。

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Me_Liu_Q

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值