线程池

本文深入解析了线程池的工作原理,包括线程池的生命周期、状态转换、任务执行流程及核心参数详解。探讨了线程池如何通过管理线程的创建、复用和回收,提高系统的响应速度和资源利用率。

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

线程池原理:

每一个 Thread 的类都有一个 start 方法。 当调用start启动线程时Java虚拟机会调用该类的 run 方法。 那么该类的 run() 方法中就是调用了 Runnable 对象的 run() 方法。

我们可以继承重写 Thread 类,在其 start 方法中添加不断循环调用传递过来的 Runnable 对象

这就是线程池的实 现原理。循环方法中不断获取 Runnable 是用 Queue 实现的,在获取下一个 Runnable 之前可以 是阻塞的。

 

 

线程生命周期(状态)
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。 在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞 (Blocked)和死亡(Dead)5种状态。尤其是当线程启动以后,它不可能一直"霸占"着CPU独自 运行,所以CPU需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换
4.1. 新建状态(NEW) 当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配 内存,并初始化其成员变量的值

4.2. 就绪状态(RUNNABLE): 当线程对象调用了start()方法之后,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和 程序计数器,等待调度运行。
4.3. 运行状态(RUNNING): 如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态。
4.4. 阻塞状态(BLOCKED): 阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。 直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状 态。阻塞的情况分三种:
等待阻塞 ( o.wait-> 等待对列 ) :
运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue) 中。
同步阻塞 (lock-> 锁池 ) 运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线 程放入锁池(lock pool)中。
其他阻塞 (sleep/join) 运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时, JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O 处理完毕时,线程重新转入可运行(runnable)状态。
线程的join方法,其实就是让调用线程等待。线程结束,调用线程即结束。
4.5. 线程死亡(DEAD) 线程会以下面三种方式结束,结束后就是死亡状态。
正常结束

run()或call()方法执行完成,线程正常结束。

异常结束

线程抛出一个未捕获的Exception或Error。

调用 stop

直接调用该线程的stop()方法来结束该线程—该方法通常容易导致死锁,不推荐使用。

 

线程终止的四种方式:
1,正常运行结束
2,使用退出标志退出,,使用了一个 Java 关键字 volatile,这个关键字的目的是使 exit 同步
3,Interrupt方法结束线程,注意该方法结束线程是通过捕获InterruptedException异常之后通过break来跳出循环,才能正 常结束run方法。
4,stop方法终止线程,原因在于stop创建子线程的线程会抛出 ThreadDeatherror 的错误,并且会释放线程所持有的所有锁,保护数据可能出现不一致性,导致其他未知的错误

 

代码块

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
    
 

代码块

    //  构造方法 存储线程池参数
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
     
 

几个核心参数的作用:

corePoolSize: 线程池核心线程数最大值
maximumPoolSize: 线程池最大线程数大小
keepAliveTime: 线程池中非核心线程空闲的存活时间大小
unit: 线程空闲存活时间单位
workQueue: 存放任务的阻塞队列
threadFactory: 用于设置创建线程的工厂,可以给创建的线程设置有意义的名字,可方便排查问题。
handler: 线城池的饱和策略事件,主要有四种类型。

 

线程池中的属性:

代码块

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    // Integer ctl  高3位数  表示 线程池状态 低29位 表示 线程数目  COUNT_BITS = 29
    private static final int COUNT_BITS = Integer.SIZE - 3;
    // 线程池中线程容量 2^29-1  可用于 与或运算 得到 高三位或 低29位 的值
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
​
    // runState is stored in the high-order bits
    // 线程池可以接受新任务
    private static final int RUNNING    = -1 << COUNT_BITS;
    // 线程池不再接收新任务  但是会执行任务队列中的任务
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    // 拒绝 并中断正在处理的任务
    private static final int STOP       =  1 << COUNT_BITS;
    // 所有任务都已被终止
    private static final int TIDYING    =  2 << COUNT_BITS;
    // 已清理完现场
    private static final int TERMINATED =  3 << COUNT_BITS;

    // 与 2^29-1 进行异或  获得线程池运行状态
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    // 与 2^29-1 进行与  获得线程池线程数目
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    // 左三位 与 右29位 或  合并成ctl
    private static int ctlOf(int rs, int wc) { return rs | wc; }
    
 

种状态的十进制值按从小到大依次排序为 RUNNING < SHUTDOWN < STOP < TIDYING <TERMINATED , 这样设计的好处是可以通过比较值的大小来确定线程池的状态

代码块

    private static boolean runStateLessThan(int c, int s) {
        return c < s;
    }
​
    private static boolean runStateAtLeast(int c, int s) {
        return c >= s;
    }
​
    private static boolean isRunning(int c) {
        return c < SHUTDOWN;
    }
    
 

RUNNING

该状态的线程池会接收新任务,并处理阻塞队列中的任务;
调用线程池的shutdown()方法,可以切换到SHUTDOWN状态;
调用线程池的shutdownNow()方法,可以切换到STOP状态;

SHUTDOWN

该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;
队列为空,并且线程池中执行的任务也为空,进入TIDYING状态;

STOP

该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;
线程池中执行的任务为空,进入TIDYING状态;

TIDYING

该状态表明所有的任务已经运行终止,记录的任务数量为0。
terminated()执行完毕,进入TERMINATED状态

TERMINATED

该状态表示线程池彻底终止

 

其他属性:

代码块

    // 任务队列
    private final BlockingQueue<Runnable> workQueue;
    // 创建线程时 使用的重入锁
    private final ReentrantLock mainLock = new ReentrantLock();
    // 工作线程集合
    private final HashSet<Worker> workers = new HashSet<Worker>();
    // 终止条件
    private final Condition termination = mainLock.newCondition();
    // 最大线程数目
    private int largestPoolSize;
    // 已完成任务数目
    private long completedTaskCount;
    // 线程工厂
    private volatile ThreadFactory threadFactory;
    // 拒绝策略
    private volatile RejectedExecutionHandler handler;
    // 保活时间
    private volatile long keepAliveTime;
    // 是否允许线程超时
    private volatile boolean allowCoreThreadTimeOut;
    // 核心池 线程数目
    private volatile int corePoolSize;
    // 最大线程池 线程数目
    private volatile int maximumPoolSize;
    // 默认拒绝策略  抛出异常
    private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();
​
    private static final RuntimePermission shutdownPerm =
        new RuntimePermission("modifyThread");
​
    private final AccessControlContext acc;
    
 

任务执行

线程池执行流程,即对应execute()方法:

 

execute方法

代码块

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        //获取线程池控制状态
        int c = ctl.get();
        //  AAAAAAAAA工作线程数目小于核心池大小
        if (workerCountOf(c) < corePoolSize) {
            //添加worker
            if (addWorker(command, true))
                return;//添加成功直接返回
            c = ctl.get();//添加失败再次获取线程池工作状态
        }
        //  BBBBBBBBBB 线程池在running状态 入队列 如果线程池处于RUNNING 状态 且 尝试将任务 放进工作队列中成功
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();//二次获取线程池工作状态
            //如果线程池不是RUNNING 状态  且  移出任务成功
            if (! isRunning(recheck) && remove(command))
                //执行拒绝策略
                reject(command);
            // 如果工作线程数目为0
            else if (workerCountOf(recheck) == 0)
                //增加worker
                addWorker(null, false);
        }
        // CCCCCCCCCCCC 队列已满 增加线程数目 执行此任务  直到线程数目到达 线程池最大值  超过 执行拒绝策略
        //如果增加worker失败
        else if (!addWorker(command, false))
            reject(command);// 线程数目超过最大池 线程数目 执行拒绝策略
    }
    
 

 

addworker

代码块

private boolean addWorker(Runnable firstTask, boolean core) {
        // 用于跳出两层循环  配合循环语旬出现的 label,类似于 goto 作用。
        retry:
        for (;;) {
            //获取线程池控制状态
            int c = ctl.get();
            //获取运行状态
            int rs = runStateOf(c);
​
            // 如线程池不是running状态  且 不同时满足(shutdown状态 且 当前任务为null  且 workqueue不为空)
            // 只有 线程池为running状态 或 为shutdown状态 且 当前任务为null  且队列还有任务未执行  才增加线程
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;  //返回失败
​
            for (;;) {
                //获取worker数目
                int wc = workerCountOf(c);
                // 如worker数目  大于最大数目 2^29-1  或  大于配置池数目  返回创建失败
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))//CAS  增加worker  线程数目
                    break retry;// 通过钩子 跳出外层循环(两层循环)
                c = ctl.get();  // Re-read ctl   二次获取线程池控制状态
                if (runStateOf(c) != rs)  如运行状态改变  钩子 到外层循环  否则继续cas增加工作线程数目
                    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);
            //  获取新worker线程
            final Thread t = w.thread;
            //  如worker线程不为null
            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()); //获取当前线程池状态
                    // 如线程池为running状态  或  shutdown状态 且任务为null
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) { 
                        if (t.isAlive()) // precheck that t is startable  线程刚创建还未启动 已为活跃状态 抛异常
                            throw new IllegalThreadStateException();
                        workers.add(w); //将work 添加到works 集合中
                        int s = workers.size(); //获取当前works集合数目
                        if (s > largestPoolSize)
                            largestPoolSize = s; //更新当前最大池大小
                        workerAdded = true; //work创建成功
                    }
                } finally {
                    mainLock.unlock(); //释放重入锁
                }
                if (workerAdded) { //如work创建成功  线程开启  执行任务
                    t.start(); // 当线程获得cpu时间片后 线程的run方法执行
                    workerStarted = true;  // worker开启成功
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);  //worker回滚
        }
        return workerStarted;  //返回worker开启状体
    }
    
 

 

为什么要先cas 增加线程数目 再 创建线程?

先加 1 '创建失败再减 l ,这是轻量处理并发创建线程的方式。如果先创建线程,成功再加 l,当发现超出限制后再销毁线程,那么这样的处理方式明显比前者代价要大。

 

work 内部类:

代码块

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        private static final long serialVersionUID = 6138294804551838833L;
​
        /** Thread this worker is running in.  Null if factory fails. */
        final Thread thread;
        /** Initial task to run.  Possibly null. */
        Runnable firstTask;
        /** Per-thread task counter */
        volatile long completedTasks;
​
        Worker(Runnable firstTask) {
            // AQS 中方法  在runworker之前 禁止线程被中断
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
​
        start() 方法调用后 执行run方法
        public void run() {
            runWorker(this);
        }
​
        // Lock methods
        //
        // The value 0 represents the unlocked state.
        // The value 1 represents the locked state.
​
        protected boolean isHeldExclusively() {
            return getState() != 0;
        }
​
        protected boolean tryAcquire(int unused) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
​
        protected boolean tryRelease(int unused) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
​
        public void lock()        { acquire(1); }
        public boolean tryLock()  { return tryAcquire(1); }
        public void unlock()      { release(1); }
        public boolean isLocked() { return isHeldExclusively(); }
​
        void interruptIfStarted() {
            Thread t;
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }
    }
 

代码块

/** Delegates main run loop to outer runWorker  */
public void run() {
    runWorker(this);  //将主运行循环委托给外部runWorker处理
}
 

调用runWorker方法:

是当前任务不为null或者从队列中取的任务不为null时,worker线程就一直去执行任务。当无要执行的任务时,尝试回收线程。

代码块

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();//获取当前线程
        Runnable task = w.firstTask;       //获取worker中首要任务
        w.firstTask = null;                //首要任务清理
        w.unlock(); // allow interrupts    //允许中断
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) { // 任务不为空或  获取任务不为空
                //获取AQS锁
                w.lock();                                        
                //线程池处于stop状态   或者   当前线程被中断 且 线程池状态是stop状态 且 当前线程是非中断状态。
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();    //当前线程请求中断
                try {
                    //开始执行任务前的Hook,类似回调函数
                    //在给定线程中执行给定Runnable之前调用的方法此方法由执行任务{@code r}的线程{@code t}调用,
                    //可用于重新初始化线程局部变量或执行日志记录。<p>这个实现什么也不做,但是可以在子类中定制注意:
                    //为了正确地嵌套多个重写,子类通常应该在这个方法的末尾调用{@code super.beforecute}。
                    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 {
                        //任务执行后的Hook,类似回调函数
                        afterExecute(task, thrown);
                    }
                } finally {
                    //执行完毕后task重置,completedTasks计数器++,解锁
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            //线程空闲达到我们设定的值时,Worker退出销毁。
            processWorkerExit(w, completedAbruptly);
        }
    }
    
 

runWorker函数中最重要的是getTask(),他不断的从阻塞队列中取任务交给线程执行。下面分析一下:

 

代码块

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状态  且  线程池不是running以及shutdown状态 或工作队列为空
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {  
                decrementWorkerCount(); //线程池数量-1,回收线程
                return null;
            }
​
            int wc = workerCountOf(c); //获取工作线程个数
​
            // Are workers subject to culling?
            //标识当前线程在空闲时,是否应该超时回收
            //如果allowCoreThreadTimeOut 为true
            //或者当前线程数量大于核心线程池数目,
            //则需要超时回收
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            //如线程数目大于最大线程池线程数目  或  允许超时回收 且 已超时
            //且 线程数目>1  或 任务队列为null
            //cas减少工作线程数 并返回Null  如cas失败  下一次循环重试
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            //去阻塞队列中取任务
            try {
                //如果允许空闲回收,则调用阻塞队列的poll,
                //否则take,一直等到队列中有可取任务
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();

                //取到任务,返回任务,
                //否则超时timedOut = true;进入下一个循环,
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }
    
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值