Java多线程-ThreadPoolExecutor详解

本文详细解析了Java线程池ThreadPoolExecutor的工作原理,包括其数据结构、线程池状态、创建方式、拒绝策略及核心方法execute()和addWorker()。通过示例代码展示了线程池的使用。

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

一)ThreadPoolExecutor简介

ThreadPoolExecutor是线程池具体实现类,继承了AbstractExecutorService抽象类。

作用:多线程的频繁运行和释放,消耗的时间会比较久。线程池主要是用来存放一定数量线程的线程集合,当线程使用时,从线程池中分配一个空闲的线程来执行对应的程序,当超过线程池的固定容量时,会有一部分线程会处于等待状态。线程池通过相应的调度策略和拒绝策略,对添加的线程进行管理。

 

二)ThreadPoolExecutor数据结构

1)阻塞队列workQueue

private final BlockingQueue<Runnable> workQueue;

描述BlockingQueue<E>是一个阻塞队列接口,常用的实现类有ArrayBlockingQueue<E>有界阻塞队列和LinkedBlockingQueue<E>无界阻塞队列。当线程池中的线程数超过它的容量的时候,线程会进入阻塞队列进行阻塞等待。

 

2)互斥锁mainLock

private final ReentrantLock mainLock = new ReentrantLock();

private final Condition termination = mainLock.newCondition(); // 线程终止条件

描述:主要是用来保证线程操作安全,实现了对线程池的互斥访问。

 

3)线程池运行任务集合workers

private final HashSet<Worker> workers = new HashSet<Worker>();

描述Worker对应了一个线程,workers包含了一个线程集合。当Worker对应的线程池启动时,它会执行线程池中的任务;当执行完一个任务后,它会从线程池的阻塞队列中取出一个阻塞的任务来继续运行。通过该方式实现了"允许多个线程同时运行"。

 

4)其它线程池参数

private int largestPoolSize; // 线程池中线程数量曾经达到过的最大值。

private long completedTaskCount; // 已完成任务数量。

private volatile ThreadFactory threadFactory; // ThreadFactory对象, 用于创建并运行线程。

private volatile RejectedExecutionHandler handler; // 线程池拒绝策略处理方式。内部实现。

private volatile long keepAliveTime; // 保持线程池中线程存活时间。当线程池处于空闲状态的时候,超过keepAliveTime时间的空闲线程都会被终止。

private volatile boolean allowCoreThreadTimeOut; // 是否允许"线程在空闲状态时,仍然能够存活"。

private volatile int corePoolSize; // 线程池核心数量,也是“线程池中实际运行的线程数量”

private volatile int maximumPoolSize; // 线程池最大数量

 

三)线程池几种状态

线程和进程的五个阶段:新就绪运行阻塞死亡

新建状态:使用new关键字或Thread及其子类创建一个线程对象后,线程为新增状态。

就绪状态:当线程调用start()方法时,该线程进入就绪状态。线程处于就绪的队列中,等待JVM线程调度器调度。

运行状态:当JVM调度器调度线程时,会执行run()方法,线程处于运行状态中。运行状态的线程可以变为阻塞状态、就绪状态和死亡状态。

阻塞状态:当线程调用一些特殊方法时,如sleep(睡眠)、wait(等待)。线程失去了占用资源之后,该线程变为阻塞状态。

1)等待阻塞:运行中的线程调用wait()方法,使线程进入到等待阻塞状态。

2)同步阻塞:当线程获取synchronized同步锁失败(同步锁被其它线程占用)。

3)其他阻塞:线程调用 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

 

线程池的5种状态:RunningSHUTDOWNSTOPTIDYINGTERMINATED

RUNNING:线程池在RUNNING状态时,能够接收新任务,并对已添加的任务进行处理。

SHUTDOWN:线程池在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。

STOP:线程池在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理中的任务。

TIDYING:当所有的任务已终止时,线程池会变为TIDYING状态。

TERMINATED:调用shutdown()方法,线程池会终止运行,就会变成TERMINATED状态。

 

四)ThreadPoolExecutor线程池创建

第一种:创建一个新的ThreadPoolExecutor与给定的初始参数和默认线程工厂和拒绝执行处理程序。

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

corePoolSize:即使空闲时仍保留在池中的线程数,除非设置 allowCoreThreadTimeOut。
maximumPoolSize:池中允许的最大线程数。
keepAliveTime:当线程数大于核心时,这是多余的空闲线程在终止之前等待新任务的最大时间。
unit:keepAliveTime参数的时间单位。
workQueue:在执行任务之前用于保存任务的队列。 该队列将仅保存execute方法提交的Runnable任务。

 

第二种:创建一个新的ThreadPoolExecutor与给定的初始参数和默认拒绝执行处理程序。

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

corePoolSize:要保留在池中的线程数,即使它们处于空闲状态,除非设置了 allowCoreThreadTimeOut。
maximumPoolSize:池中允许的最大线程数。
keepAliveTime:当线程数大于核心时,这是多余的空闲线程在终止之前等待新任务的最大时间。
unit:keepAliveTime参数的时间单位。
workQueue:在执行任务之前用于保存任务的队列。 这个队列只会保存execute方法提交的Runnable任务。 
threadFactory:执行程序创建新线程时使用的工厂。

 

第三种:创建一个新的 ThreadPoolExecutor与给定的初始参数和默认线程工厂。

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

corePoolSize:要保留在池中的线程数,即使它们处于空闲状态,除非设置了 allowCoreThreadTimeOut。
maximumPoolSize:池中允许的最大线程数。
keepAliveTime:当线程数大于核心时,这是多余的空闲线程在终止之前等待新任务的最大时间。
unit:keepAliveTime参数的时间单位。
workQueue:在执行任务之前用于保存任务的队列。 这个队列只会保存execute方法提交的Runnable任务。 
threadFactory:执行程序创建新线程时使用的工厂。
handler:执行被阻止时使用的处理程序,因为达到线程限制和队列容量。

 

第四种:创建一个新 ThreadPoolExecutor给定的初始参数。最终都是调用该方法。

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; // 即使空闲时仍保留在池中的线程数,除非设置 allowCoreThreadTimeOut。
    this.maximumPoolSize = maximumPoolSize; // 池中允许的最大线程数。
    this.workQueue = workQueue; // 用于在执行任务之前使用的队列。 这个队列将仅保存execute方法提交的Runnable任务。
    this.keepAliveTime = unit.toNanos(keepAliveTime); // keepAliveTime参数的时间单位,当线程数大于内核时,这是多余的空闲线程在终止前等待新任务的最大时间。
    this.threadFactory = threadFactory; // 执行程序创建新线程时使用的工厂。
    this.handler = handler; // 执行被阻止时使用的处理程序,因为达到线程限制和队列容量。
}

 

五)ThreadPoolExecutor拒绝策略

该类提供了四种线程池拒绝策略

1)ThreadPoolExecutor.AbortPolicy:当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException异常。

2)ThreadPoolExecutor.CallerRunsPolicy:当任务添加到线程池中被拒绝时,会在线程池当前正在运行的Thread线程池中处理被拒绝的任务。

3)ThreadPoolExecutor.DiscardOldestPolicy:当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。

4)ThreadPoolExecutor.DiscardPolicy:当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务。

 

六)execute()、addWorker()

1)void execute(Runnable command)

将任务添加到线程池中,并在将来某个时候执行给定的任务。

public void execute(Runnable command) {
    if (command == null) // 判断添加的任务是否为空, 为空直接抛异常
        throw new NullPointerException();
        
    int c = ctl.get(); // 获取线程池中的任务数量
    if (workerCountOf(c) < corePoolSize) { // 判断运行的线程数是否小于核心数量
        if (addWorker(command, true)) // 当线程数小于核心数量,添加一个新线程,并启动该线程
            return;
        c = ctl.get(); // 重新获取线程池中的任务数量
    }
    if (isRunning(c) && workQueue.offer(command)) { // 当有运行的线程时,把任务添加到阻塞队列中
        int recheck = ctl.get(); // 重新获取线程池中的任务数量
        if (! isRunning(recheck) && remove(command)) // 如果线程因某种原因中断了,就删除任务
            reject(command); // 删除任务之后,根据线程池的拒绝策略返回信息
        else if (workerCountOf(recheck) == 0) // 当线程池中没有运行的任务时,添加一个null任务
            addWorker(null, false);
    }
    else if (!addWorker(command, false)) // 新创建一个线程,并启动,如启动失败,根据线程池拒绝策略返回信息
        reject(command);
}

 

2)boolean addWorker(Runnable firstTask, boolean core)

添加线程任务,并启动执行给定的任务

private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) { // 一直运行线程池,除非调用了shutdown()关闭线程池方法
        int c = ctl.get(); // 获取线程池中任务数量
        int rs = runStateOf(c); // 获取线程池运行状态

        // Check if queue empty only if necessary. // 数据有效性检查
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
                firstTask == null &&
                ! workQueue.isEmpty()))
            return false;

        for (;;) {
            int wc = workerCountOf(c); // 获取运行的线程数量
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize)) // 当线程数量超过时,返回false
                return false;
            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 {
        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()); // 获取线程运行状态

                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                if (t.isAlive()) // precheck that t is startable
                    throw new IllegalThreadStateException();
                workers.add(w); // 将线程添加线程集合中
                int s = workers.size();
                if (s > largestPoolSize)
                    largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) { // 当线程检查OK时,启动该线程
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted) // 当线程因某种原先失败时,把任务删除,并从线程集合中移除,并终止线程
            addWorkerFailed(w);
    }
    return workerStarted;
}

 

七)ThreadPoolExecutor实现

第一步:创建了一个类,继承Runnable接口,实现run方法。

第二步:创建一个ThreadPoolExecutor线程池,指定核心线程池大小,最大线程池大小,一些默认参数。

第三步:循环创建多个线程实例,并把线程一个个添加到线程池中,通过线程池来启动运行。

源码

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPool {

    // 自定义类
    static class MyRunnable implements Runnable {
        private String threadName;
        public MyRunnable(String threadName) {
            this.threadName = threadName;
        }

        @Override
        public void run() {
            try {
                System.out.println("当前线程名" + Thread.currentThread().getName() + ", 任务" + threadName + " is running!");
                Thread.sleep(100); // 睡眠100毫秒,方便测试
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        // 创建一个ThreadPoolExecutor线程池
        ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 0L, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<Runnable>(10), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

        MyRunnable my = null;
        for (int i = 0; i < 5; i++) {
            my = new MyRunnable("MyRunnable" + (i+1));
            pool.execute(my); // 将线程添加到线程中
        }

        // 关闭线程池
        pool.shutdown();
    }
}

打印效果图:

 

识别二维码关注个人微信公众号

本章完结,待续,欢迎转载!
 
本文说明:该文章属于原创,如需转载,请标明文章转载来源!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值