java--线程池技术汇总

一、为什么使用线程池?

为什么使用线程池
说明

我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。

那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?

在Java中可以通过线程池来达到这样的效果。在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池。

以网络请求为例

网络请求为例通常有两种形式

第一种:请求不频繁,且每次连接后会保持相当一段时间来读数据或者写数据,最后断开,如文件下载,网络流媒体等。

另一种:请求频繁,但是连接上以后读/写很少量的数据就断开连接。考虑到服务的并发问题,如果每个请求来到以后服务都为它启动一个线程,那么这对服务的资源可能会造成很大的浪费,特别是第二种情况。

通常情况下,创建线程是需要一定的耗时的,设这个时间为T1,而连接后读/写服务的时间为T2,当T1>>T2时,我们就应当考虑一种策略或者机制来控制,使得服务对于第二种请求方式也能在较低的功耗下完成。

可以用线程池来解决这个问题,首先,在服务启动的时候,我们可以启动多个线程,并用一个容器(如线程池)来管理这些线程。当请求到来时,可以从池中取一个线程出来,执行任务(通常是对请求的响应),当任务结束后,再将这个线程放入池中备用;如果请求到来而池中没有空闲的线程,该请求需要排队等候。最后,当服务关闭时销毁该池即可。

二、线程池的优缺点?

作用(优点)
降低资源消耗
通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
提高响应速度
当任务到达时,任务可以不需要等到线程创建就能立即执行。
提高线程的可管理性
线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
缺点
多线程的缺点

多线程会占CPU,使用多线程的地方并发量比较高时会导致其他功能响应很慢。

线程池本身的缺点

适用于生存周期较短的的任务,不适用于又长又大的任务。
不能对于线程池中任务设置优先级。
不能标识线程的各个状态,比如启动线程,终止线程。
对于任意给定的应用程序域,只能允许一个线程池与之对应。
线程池所有线程都处于多线程单元中,如果想把线程放到单线程单元中,线程池就废掉了。

三、核心参数有哪些?

核心参数
ThreadPoolExecutor原型:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

corePoolSize
线程池的核心线程数
即便是线程池里没有任何任务,也会有corePoolSize个线程在候着等任务。
maximumPoolSize
最大线程数。
超过此数量,会触发拒绝策略。
keepAliveTime
线程的存活时间。
当线程池里的线程数大于corePoolSize时,如果等了keepAliveTime时长还没有任务可执行,则线程退出。
unit
指定keepAliveTime的单位
比如:秒:TimeUnit.SECONDS。
workQueue
一个阻塞队列,提交的任务将会被放到这个队列里。
threadFactory
线程工厂,用来创建线程
主要是为了给线程起名字,默认工厂的线程名字:pool-1-thread-3。
handler
拒绝策略
当线程池里线程被耗尽,且队列也满了的时候会调用。
默认拒绝策略为AbortPolicy。即:不执行此任务,而且抛出一个运行时异常
java doc的建议

java doc不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池,例如:Executors.newCachedThreadPool(); Executors.newSingleThreadExecutor(); 等。

大公司的建议

很多大公司会明确要求使用创建ThreadPoolExecutor对象的方法来使用线程池,因为这样能使人明确各个参数的具体含义。Executors类中提供的静态方法都是调用了创建ThreadPoolExecutor对象的方法,例如:

public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}

个数(大小)设置

任务分类

根据任务所需要的cpu和io资源的量可以分为:

CPU密集型任务:
主要是执行计算任务。响应时间很快,cpu一直在运行,这种任务cpu的利用率很高。
线程池大小太大对程序性能而言,反而是不利的,但最少也不应低于处理器的核心数。因为当有多个任务处于就绪状态时,处理器核心需要在线程间频繁进行上下文切换,而这种切换对程序性能损耗较大。
IO密集型任务
主要是进行IO操作,执行IO操作的时间较长,这时cpu处于空闲状态,导致cpu的利用率不高。
当一个任务执行IO操作时,其线程将被阻塞,于是处理器可以立即进行上下文切换以便处理其他就绪线程。如果我们只有处理器可用核心数那么多个线程的话,即使有待执行的任务也无法处理,因为我们已经拿不出更多的线程供处理器调度了。
CPU密集型任务与IO密集型任务区分方法

如果任务被阻塞的时间少于执行时间,即这些任务是计算密集型的,则程序所需线程数将随之减少,但最少也不应低于处理器的核心数。

如果任务被阻塞的时间大于执行时间,即该任务是IO密集型的,我们就需要创建比处理器核心数大几倍数量的线程。例如,如果任务有50%的时间处于阻塞状态,则程序所需线程数为处理器可用核心数的两倍。

线程池使用步骤

步骤

1、创建一个线程池对象,控制要创建几个线程对象。

2、实现线程:

法1:新建一个类实现Runnable或者Callable接口。
法2:直接用lambda表达式传值。比如:

public class Demo {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);
        executor.execute(Demo::myRun);
    }
 
    public static void myRun() {
        System.out.println("hello world");
    }
}

3、提交线程调用如下任一方法即可

void execute(Runnable command);
Future<?> submit(Runnable task)
Future submit(Callable task)
Future submit(Runnable task, T result)
4、结束线程池

submit 与 execute
在这里插入图片描述

Future

Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。还提供了其他方法,以确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。

如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。

也就是说Future提供了三种功能:

判断任务是否完成;
中断任务;
获取任务执行结果
future的方法

boolean cancel(boolean mayInterruptIfRunning) 试图取消对此任务的执行。
V get() 如有必要,等待计算完成,然后获取其结果。
V get(long timeout, TimeUnit unit) 如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。
boolean isCancelled() 如果在任务正常完成前将其取消,则返回 true。
boolean isDone() 如果任务已完成,则返回 true。
关闭线程池

void shutdown() 启动一次顺序关闭,等待执行以前提交的任务完成,但不接受新任务。
List shutdownNow() 试图立即停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。
示例(ThreadPoolExecutor)
简介

ThreadPoolExecutor 结构图

在这里插入图片描述

1,如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。
2,如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。
3,如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。
4,如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。
示例1:睡眠任务
代码

package com.example;
 
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
 
class MyTask implements Runnable {
    private int taskNum;
 
    public MyTask(int num) {
        this.taskNum = num;
    }
 
    @Override
    public void run() {
        System.out.println("正在执行task " + taskNum);
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-----------------------task " + taskNum + "执行完毕");
    }
}
 
public class Demo {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10,
                200, TimeUnit.MILLISECONDS,
                new LinkedBlockingDeque<>(4));
 
        for (int i = 0; i < 14; i++) {
            MyTask myTask = new MyTask(i);
            executor.execute(myTask);
            System.out.println("线程池中线程数目:" + executor.getPoolSize() + ",队列中等待执行的任务数目:" +
                    executor.getQueue().size() + ",已执行完别的任务数目:" + executor.getCompletedTaskCount());
        }
        executor.shutdown();
    }
}

运行结果

Connected to the target VM, address: '127.0.0.1:58452', transport: 'socket'
正在执行task 0
线程池中线程数目:1,队列中等待执行的任务数目:0,已执行完别的任务数目:0
线程池中线程数目:2,队列中等待执行的任务数目:0,已执行完别的任务数目:0
线程池中线程数目:3,队列中等待执行的任务数目:0,已执行完别的任务数目:0
正在执行task 1
线程池中线程数目:4,队列中等待执行的任务数目:0,已执行完别的任务数目:0
正在执行task 2
线程池中线程数目:5,队列中等待执行的任务数目:0,已执行完别的任务数目:0
正在执行task 3
正在执行task 4
线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:2,已执行完别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:3,已执行完别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:4,已执行完别的任务数目:0
线程池中线程数目:6,队列中等待执行的任务数目:4,已执行完别的任务数目:0
线程池中线程数目:7,队列中等待执行的任务数目:4,已执行完别的任务数目:0
正在执行task 9
正在执行task 10
线程池中线程数目:8,队列中等待执行的任务数目:4,已执行完别的任务数目:0
线程池中线程数目:9,队列中等待执行的任务数目:4,已执行完别的任务数目:0
正在执行task 11
正在执行task 12
线程池中线程数目:10,队列中等待执行的任务数目:4,已执行完别的任务数目:0
正在执行task 13
-----------------------task 3执行完毕
-----------------------task 2执行完毕
-----------------------task 1执行完毕
正在执行task 5
-----------------------task 4执行完毕
正在执行task 7
正在执行task 8
-----------------------task 0执行完毕
-----------------------task 11执行完毕
-----------------------task 10执行完毕
-----------------------task 9执行完毕
正在执行task 6
-----------------------task 13执行完毕
-----------------------task 12执行完毕
-----------------------task 5执行完毕
-----------------------task 8执行完毕
-----------------------task 6执行完毕
-----------------------task 7执行完毕
Disconnected from the target VM, address: '127.0.0.1:58452', transport: 'socket'
 
Process finished with exit code 0

从执行结果可以看出:

1,当线程池中线程的数目大于5时,便将任务放入任务缓存队列里面,当任务缓存队列满了之后,便创建新的线程。
2,线程都执行完毕后,程序可以正常退出。
如果上面程序中,将for循环改成15,就会抛出任务拒绝异常了,日志如下:

Connected to the target VM, address: '127.0.0.1:58469', transport: 'socket'
正在执行task 0
线程池中线程数目:1,队列中等待执行的任务数目:0,已执行完别的任务数目:0
线程池中线程数目:2,队列中等待执行的任务数目:0,已执行完别的任务数目:0
线程池中线程数目:3,队列中等待执行的任务数目:0,已执行完别的任务数目:0
正在执行task 1
正在执行task 2
线程池中线程数目:4,队列中等待执行的任务数目:0,已执行完别的任务数目:0
正在执行task 3
线程池中线程数目:5,队列中等待执行的任务数目:0,已执行完别的任务数目:0
正在执行task 4
线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:2,已执行完别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:3,已执行完别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:4,已执行完别的任务数目:0
线程池中线程数目:6,队列中等待执行的任务数目:4,已执行完别的任务数目:0
线程池中线程数目:7,队列中等待执行的任务数目:4,已执行完别的任务数目:0
正在执行task 9
线程池中线程数目:8,队列中等待执行的任务数目:4,已执行完别的任务数目:0
正在执行task 10
线程池中线程数目:9,队列中等待执行的任务数目:4,已执行完别的任务数目:0
正在执行task 11
线程池中线程数目:10,队列中等待执行的任务数目:4,已执行完别的任务数目:0
正在执行task 12
正在执行task 13
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.example.MyTask@27ddd392 rejected from java.util.concurrent.ThreadPoolExecutor@19e1023e[Running, pool size = 10, active threads = 10, queued tasks = 4, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
	at com.example.Demo.main(Demo.java:34)
-----------------------task 4执行完毕
-----------------------task 1执行完毕
-----------------------task 9执行完毕
-----------------------task 0执行完毕
-----------------------task 13执行完毕
-----------------------task 3执行完毕
-----------------------task 2执行完毕
-----------------------task 12执行完毕
-----------------------task 11执行完毕
-----------------------task 10执行完毕
正在执行task 8
正在执行task 6
正在执行task 5
正在执行task 7
-----------------------task 7执行完毕
-----------------------task 6执行完毕
-----------------------task 5执行完毕
-----------------------task 8执行完毕

结果

在执行第15个线程时,直接抛出了异常,但没有影响其他进程的运行
程序无法正常退出
示例2:死循环任务
本处只是修改了上边的睡眠任务为死循环,其他不变

package com.example;
 
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
 
class MyTask implements Runnable {
    private int taskNum;
 
    public MyTask(int num) {
        this.taskNum = num;
    }
 
    @Override
    public void run() {
        System.out.println("正在执行task " + taskNum);
        while (true) ;
    }
}
 
public class Demo {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10,
                200, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(4));
 
        for (int i = 0; i < 14; i++) {
            MyTask myTask = new MyTask(i);
            executor.execute(myTask);
            System.out.println("线程池中线程数目:" + executor.getPoolSize() + ",队列中等待执行的任务数目:" +
                    executor.getQueue().size() + ",已执行完别的任务数目:" + executor.getCompletedTaskCount());
        }
        executor.shutdown();
    }
}

执行结果:(程序无法退出)

Connected to the target VM, address: '127.0.0.1:60128', transport: 'socket'
正在执行task 0
线程池中线程数目:1,队列中等待执行的任务数目:0,已执行完别的任务数目:0
线程池中线程数目:2,队列中等待执行的任务数目:0,已执行完别的任务数目:0
线程池中线程数目:3,队列中等待执行的任务数目:0,已执行完别的任务数目:0
正在执行task 1
线程池中线程数目:4,队列中等待执行的任务数目:0,已执行完别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:0,已执行完别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:2,已执行完别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:3,已执行完别的任务数目:0
正在执行task 3
线程池中线程数目:5,队列中等待执行的任务数目:4,已执行完别的任务数目:0
正在执行task 4
线程池中线程数目:6,队列中等待执行的任务数目:4,已执行完别的任务数目:0
正在执行task 2
线程池中线程数目:7,队列中等待执行的任务数目:4,已执行完别的任务数目:0
线程池中线程数目:8,队列中等待执行的任务数目:4,已执行完别的任务数目:0
线程池中线程数目:9,队列中等待执行的任务数目:4,已执行完别的任务数目:0
线程池中线程数目:10,队列中等待执行的任务数目:4,已执行完别的任务数目:0
正在执行task 9
正在执行task 10
正在执行task 12
正在执行task 11
正在执行task 13

四、如何设置线程池的大小?

常用线程池大小设置

CPU密集型:核心线程数 = CPU核数 + 1
IO密集型:核心线程数 = CPU核数 * 2 + 1
CPU核数可以用此法获得:Runtime.getRuntime().availableProcessors()

对于计算密集型的任务,一个有N个处理器的系统通常使用一个N+1个线程的线程池来获得最优的利用率。+1的原因:如果计算密集型的线程恰好在某时因为发生一个页错误或者因其它原因而暂停,刚好有一个"额外"的线程,可以确保在这种情况下CPU周期不会中断工作。

计算公式

N = CPU的数量

U = 期望的CPU的使用率,介于0-1之间

f:阻塞系数(阻塞时间占总时间的比例。总时间:阻塞时间 + 执行时间)

线程池大小 = N * U / (1 - f) //一个完全阻塞的任务是注定要挂掉的,无须担心阻塞系数会达到1。

举例:CPU核心数是4,期望cpu的使用率是100%,等待时间是4秒,计算时间是1秒。那么最优的池大小就是:

4 * 100% / (1 - 4/5) = 20

五、线程池的执行流程是怎样的?

本文介绍Java线程池的原理,包括:线程池的流程、线程池的结构、线程池的任务状态。

执行流程
流程图
在这里插入图片描述
1,提交任务
2,线程池判断核心线程池(核心线程数)里的线程是否已经满了(全都在执行任务)。
如果不是:看线程数是否到达指定的核心线程池的大小
如果不是:则创建一个新的线程来执行任务。
如果是:使用空闲的线程来执行任务
如果是,则进入下个流程。
3,线程池判断工作队列是否已满。
如果没有满,则将新提交的任务存储在这工作队列里。
如果工作队列满了,则进入下一个流程。
4,线程池判断线程池的所有线程(最大线程数)是否已经满了(全都在执行任务)。
如果不是:看线程数是否到达指定的最大线程池的大小

  1. 如果不是:则创建一个新的线程来执行任务。
  2. 如果是:使用空闲的线程来执行任务

如果是:则交给饱和策略来处理这个任务。

线程池结构

组成部分

一个线程池包括四个基本部分

线程管理池(ThreadPool)
用于创建并管理线程池,有创建,销毁,添加新任务;
工作线程(PoolWorker)
线程池中的线程在没有任务的时候处于等待状态,可以循环的执行任务;
任务接口(Task)
每个任务必须实现接口,用来提供工作线程调度任务的执行,规定了任务的入口以及执行结束的收尾工作和任务的执行状态等;
任务队列
用于存放没有处理的任务,提供一种缓存机制。
类的关系
在这里插入图片描述
Executor是一个顶层接口
只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型
从字面意思可以理解,就是用来执行传进去的任务的。
ExecutorService接口继承了Executor接口
声明了一些方法:submit、invokeAll、invokeAny以及shutDown等
抽象类AbstractExecutorService实现了ExecutorService接口
实现了ExecutorService中声明的所有方法
ThreadPoolExecutor继承了类AbstractExecutorService
比较重要的类:
线程池状态
状态图

在这里插入图片描述
在这里插入图片描述
线程池的5种状态:Running、ShutDown、Stop、Tidying、Terminated。

在这里插入图片描述

六、什么时候会达到最大线程数?

七、饱和策略(拒绝策略)是怎样的?

在这里插入图片描述
1. Abort策略:默认策略。
新任务提交时直接抛出未检查的异常RejectedExecutionException,该异常可由调用者捕获。

在主函数中添加如下代码:

policy(new ThreadPoolExecutor.AbortPolicy());

2. Discard策略:抛弃新提交的任务
在main函数中运行

policy(new ThreadPoolExecutor.DiscardPolicy());

3. DiscardOldest策略
丢弃最老(队头)的任务,然后尝试提交新的任务。(不适合工作队列为优先队列场景)

在main函数中运行如下方法

policy(new ThreadPoolExecutor.DiscardOldestPolicy());

4. CallerRuns策略
在主函数运行:

policy((new ThreadPoolExecutor.CallerRunsPolicy()));

八、如何进行异常处理?

execute和submit有什么区别?

execute

execute提交的任务,会被封装成一个Runable任务,然后Runable对象被封装成一个Worker,最后在Worker的run方法里面跑runWoker方法, 里面再又调了我们最初的参数 Runable任务的任务,并且用try-catch捕获了异常,会被直接抛出去,因此我们在execute中看到了我们的任务的异常信息。

submit

submit提交的任务,会被封装成一个Runable任务,然后Runable对象被封装成一个FutureTask ,里面的 run 方法 try-catch了所有的异常,并设置到了outcome(Object类型)里面, 可以通过FutureTask .get获取到outcome。

所以在submit提交的时候,里面发生了异常, 是不会有任何抛出信息的。在submit里面,除了从返回结果里面取到异常之外, 没有其他方法了。

因此,在不需要返回结果的情况下,最好用execute ,这样如果疏漏了异常捕获,也不至于丢掉异常信息。

九、keepAlive参数的作用是什么?

worker 线程会从阻塞队列中获取需要执行的任务,这个方法不是简单的 take 数据。

     你也许好奇是怎样判断线程有多久没有活动了,是不是以为线程池会启动一个监控线程,专门监控哪个线程正在偷懒?想太多,其实只是在线程从工作队列 poll 任务时,加上了超时限制,如果线程在 keepAliveTime 的时间内 poll 不到任务,那我就认为这条线程没事做,可以干掉了 。

源码分析

ThreadPoolExecutor#getTask()

 private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?
 
        for (;;) {//自旋
            int c = ctl.get();
            int rs = runStateOf(c);
            /** 对线程池状态的判断,两种情况会 workerCount-1,并且返回 null
 			 1. 线程池状态为 shutdown,且 workQueue 为空(反映了 shutdown 状态的线程池还是
			要执行 workQueue 中剩余的任务的)
			 2. 线程池状态为 stop(shutdownNow()会导致变成 STOP)(此时不用考虑 workQueue
			的情况)
			**/
 
            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;;//返回 null,则当前 worker 线程会退出
            }
 
            int wc = workerCountOf(c);
 
			// timed 变量用于判断是否需要进行超时控制。
			// allowCoreThreadTimeOut 默认是 false,也就是核心线程不允许进行超时;
 			// wc > corePoolSize,表示当前线程池中的线程数量大于核心线程数量;
 			// 对于超过核心线程数量的这些线程,需要进行超时控制
            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
 
			/**
			  1. 线程数量超过 maximumPoolSize 可能是线程池在运行时被调用了 setMaximumPoolSize()
			  被改变了大小,否则已经 addWorker()成功不会超过 maximumPoolSize
			  2. timed && timedOut 如果为 true,表示当前操作需要进行超时控制,并且上次从阻塞队列中
			  获取任务发生了超时.其实就是体现了空闲线程的存活时间
			**/
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }
 
            try {
            /**
			根据 timed 来判断,如果为 true,则通过阻塞队列 poll 方法进行超时控制,如果在
			keepaliveTime 时间内没有获取到任务,则返回 null.
			否则通过 take 方法阻塞式获取队列中的任务
			**/
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null) //如果拿到的任务不为空,则直接返回给 worker 进行处理
                    return r;
                timedOut = true;// 如果 r==null,说明已经超时了,队列中仍然没有任务,此时设置timedOut=true,在下次自旋的时候进行回收
                
            } catch (InterruptedException retry) {
            // 如果获取任务时当前线程发生了中断,则设置 timedOut 为false 并返回循环重试
                timedOut = false;
            }
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值