线程池的简单介绍

1. 线程池

1.1 ThreadPoolExecutor

1.1.1 构造方法

在ThreadPoolExecutor类中提供了四个构造方法:

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) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         threadFactory, defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), handler);
}
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.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

构造器中各个参数的含义:

  1. corePoolSize:核心线程的大小。默认情况下,在创建了线程池后,线程池中并没有任何线程,而是等待有任务到来后才创建线程去执行任务。除非调用了 prestartAllCoreThreads()、prestartCoreThread() 方法----在没有任务到来之前,就创建 corePoolSize 个线程或一个线程。

  2. maximumPoolSize:线程池中最多能创建的线程数

  3. keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于 corePoolSize 时,keepAliveTime 才会起作用,直到线程池中的线程数不大于 corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0

  4. unit:参数keepAliveTime的时间单位

  5. workQueue:阻塞队列。它用来存储等待执行的任务

    阻塞队列:ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、PriorityBlockingQueue
    FIFO:ArrayBlockingQueue、LinkedBlockingQueue
    优先级:PriorityBlockingQueue
    放和取交替:SynchronousQueue

    ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和Synchronous

  6. threadFactory:线程工厂,主要用来创建线程

  7. handler:表示当拒绝处理任务时的策略

    ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常
    ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常
    ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

1.1.2 方法

在 ThreadPoolExecutor 类中的几个非常重要的方法:

  • execute():实际上是Executor中声明的方法。这个方法是 ThreadPoolExecutor 的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行
  • submit():实际上是在ExecutorService中声明的方法,在 AbstractExecutorService 就已经有了具体的实现。这个方法也是用来向线程池提交任务的,但是它和 execute() 方法不同,它能够返回任务执行的结果,去看 submit() 方法的实现,会发现它实际上还是调用的 execute() 方法,只不过它利用了 Future 来获取任务执行结果
  • shutdown() 和 shutdownNow() 是用来关闭线程池的

1.1.3 成员变量

ThreadPoolExecutor 中的重要成员变量

AtomicInteger ctl

AtomicInteger 类型的 ctl 代表了 ThreadPoolExecutor 中的控制状态,它是一个复合类型的成员变量(原子整数),借助高低位包装了两个概念:

  1. workerCount:线程池中当前活动的线程数量,占据ctl的低29位(COUNT_BITS = Integer.SIZE - 3 就代表了workerCount所占位数)
  2. runState:线程池运行状态,占据ctl的高3位,有RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED五种状态

workerCount:代表了线程池中当前活动的线程数量。它的下限阈值:0;它的上限阈值:CAPACITY = (1 << COUNT_BITS) - 1

1 左移 29 位:00000000 00000000 00000000 00000001 => 0010000 00000000 00000000 00000000

再减去 1:00011111 11111111 11111111 11111111

前三位代表线程池运行状态 runState,所以这里 workerCount 的理论最大值就应该是 29 个 1,即 536870911

从 AtomicInteger ctl 中解析出 workerCount 的方法:

private static int workerCountOf(int c)  { return c & CAPACITY; }

计算逻辑:参数 c 代表 ctl 的值,将其与 CAPACITY 进行与操作 &(与 00011111 11111111 11111111 11111111 进行与操作)。c 的前三位通过与 000 进行与操作,无论 c 前三位为何值,最终都会变成 000,也就是舍弃前三位的值;而 c 的低 29 位与 29 个 1 进行与操作,c 的低 29 位还是会保持原值,这样就从AtomicInteger ctl 中解析出了 workerCount 的值。

runState:线程池运行状态。五种状态:RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED

  • RUNNING:接受新任务,并处理队列任务
private static final int RUNNING    = -1 << COUNT_BITS;

-1 在 Java 底层是由 32 个 1 表示的,左移 29 位的话,即11100000 00000000 00000000 00000000,也就是低 29 位全部为 0,高 3 位全部为 1 的话,表示RUNNING 状态,即 -536870912

  • SHUTDOWN:不接受新任务,但会处理队列任务

  • STOP:不接受新任务,不会处理队列任务,而且会中断正在处理过程中的任务

  • TIDYING:所有的任务已结束,workerCount 为 0,线程过渡到 TIDYING 状态,将会执行 terminated() 钩子方法

  • TERMINATED:terminated() 方法已经完成

线程池状态转换:

  • RUNNING => SHUTDOWN:调用了 shutdown() 方法;线程池实现了 finalize() 方法,在里面调用了shutdown() 方法
  • (RUNNING or SHUTDOWN) => STOP:调用shutdownNow()方法
  • SHUTDOWN => TIDYING:线程池和队列均为空
  • STOP => TIDYING:线程池为空
  • TIDYING => TERMINATED:terminated() 钩子方法完成

线程池的关闭

ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow()

  • shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
  • shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务
BlockingQueue workQueue

workQueue 是用于持有任务并将其转换成工作线程 worker 的队列

volatile int corePoolSize

核心线程池大小,保持存活的工作线程的最小数目,当小于 corePoolSize 时,会直接启动新的一个线程来处理任务,而不管线程池中是否有空闲线程

1.1.4 线程池工作原理

查看 ThreadPoolExecutor.execute() 方法:

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();

    int c = ctl.get();
    // 判断当前活跃线程数是否小于 corePoolSize
    if (workerCountOf(c) < corePoolSize) {
        // 如果小于,则调用 addWork() 方法创建线程执行任务
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    
    // 如果大于等于 corePoolSize,则将任务添加到 workQueue 队列
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0) {
            addWorker(null, false);
        }
    }
    
    // 如果放入 workQueue 队列失败,则创建非核心线程执行任务
    else if (!addWorker(command, false)) {
        // 如果这时创建线程失败(当前线程数大于等于 maximumPoolSize),则调用 reject() 方法
        reject(command);
    }
}

大概就是以上逻辑吧~~

1.1.5 线程池案例

public class MyTask implements Runnable {

    private int taskNum;

    public MyTask(int taskNum) {
        this.taskNum = taskNum;
    }

    @Override
    public void run() {
        System.out.println("正在执行 task " + taskNum);
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("task " + taskNum + "执行完毕");
        }
    }
}
public class Test {

    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(5));

        for (int i = 0; i < 15 ; i++) {
            MyTask myTask = new MyTask(i);
            executor.execute(myTask);

            System.out.println("线程池中线程数目:" + executor.getPoolSize() + ",队列中等待执行的任务数目:" +
                    executor.getQueue().size() + ",已执行玩别的任务数目:" + executor.getCompletedTaskCount());
        }

        executor.shutdown();
    }
}

从执行结果可以看出,当线程池中线程的数目大于5时,便将任务放入任务缓存队列里面,当任务缓存队列满了之后,便创建新的线程。如果上面程序中,将for循环中改成执行20个任务,就会抛出任务拒绝异常了。

不过在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池:

Executors.newCachedThreadPool():创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE

Executors.newSingleThreadExecutor():创建容量为1的缓冲池

Executors.newFixedThreadPool(int):创建固定容量大小的缓冲池

下面是这三个静态方法的具体实现:

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

从它们的具体实现来看,它们实际上也是调用了ThreadPoolExecutor,只不过参数都已配置好了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值