作用
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗
- 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控
使用
通过Executors类,提供四种线程池
方式 | 特点 |
---|---|
newCachedThreadPool | 可缓存线程池,线程池长度超过处理需要,可回收线程,线程池为无限大,当执行第二个任务的时候,第一个任务已经完成,会复用第一个任务的线程,而不用重新创建 |
newFixedThreadPool | 定长线程池,可控制线程最大并发数,超出的线程会在队列中等待 |
newScheduledThreadPool | 定长线程池,支持定时及周期性任务执行 |
newSingleThreadExecutor | 单例线程池,用唯一的工作线程执行任务,保证所有任务按照指定顺序执行(FIFO或者LIFO) |
public class Task extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class TestCachedThreadPool {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
Task task = new Task();
executorService.execute(task);
}
//pool-1-thread-1 is running
//pool-1-thread-5 is running
//pool-1-thread-2 is running
//pool-1-thread-4 is running
//pool-1-thread-3 is running
//必须显式结束,不然程序永远不会结束
executorService.shutdown();
}
}
这个看起来好像没有用到线程池,其实是因为没有可复用的线程,所以就一直创建新的线程了
public class TestFixedThreadPool {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
for (int i = 0; i < 5; i++) {
Task task = new Task();
executorService.execute(task);
}
//pool-1-thread-1 is running
//pool-1-thread-2 is running
//pool-1-thread-1 is running
//pool-1-thread-2 is running
//pool-1-thread-1 is running
executorService.shutdown();
}
}
public class TestScheduledThreadPool {
public static void main(String[] args) {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
//任务,第1次任务延迟的时间,2次任务间隔时间,时间单位
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("task 1 " + System.currentTimeMillis());
}
}, 1, 5, TimeUnit.SECONDS);
//两者互不影响
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("task 2 " + System.currentTimeMillis());
}
}, 1, 2,TimeUnit.SECONDS);
//task 1 1521949174111
//task 2 1521949174112
//task 2 1521949176106
//task 2 1521949178122
//task 1 1521949179104
//task 2 1521949180114
}
}
public class TestSingleThreadExecutor {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
Task task = new Task();
executorService.execute(task);
}
//pool-1-thread-1 is running
//pool-1-thread-1 is running
//pool-1-thread-1 is running
//pool-1-thread-1 is running
//pool-1-thread-1 is running
executorService.shutdown();
}
}
源码
状态
状态 | 含义 | 值 |
---|---|---|
RUNNING | 接受新任务,并且处理进入队列的任务 | -536870912 |
SHUTDOWN | 不接受新任务,处理进入队列的任务 | 0 |
STOP | 不接受新任务,不处理进入队列的任务,并且中断正在执行的任务 | 536870912 |
TIDYING | 所有任务执行完成,workerCount为0。线程转到了状态TIDYING会执行terminated()钩子方法 | 1073741824 |
TERMINATED | terminated()已经执行完成 | 1610612736 |
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
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;
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
AtomicInteger类型的ctl代表了ThreadPoolExecutor的控制状态,是一个复合类型的变量,借助高低位包装了2个概念
1. runState 线程池运行状态,占据ctrl的高三位
2. workerCount 线程池中当前活动的线程数量,占据ctl的低29位
COUNT_BITS代表了workerCount所占的位数,即29,而CAPACITY表示线程池理论的最大活动线程数量,即536870911
0在Java底层是由32个0表示的,无论左移多少位,还是32个0,即SHUTDOWN的值为0,TIDYING则是高三位为010,低29为0
private static int runStateOf(int c) { return c & ~CAPACITY; }
~是按位取反的意思,CAPACITY表示的是高位的3个0,和低位的29个1,而~CAPACITY则表示高位的3个1,2低位的9个0,然后再与入参c执行按位与操作,即高3位保持原样,低29位全部设置为0,也就获取了线程池的运行状态runState。
private static int ctlOf(int rs, int wc) { return rs | wc; }
传入的rs表示线程池运行状态runState,其是高3位有值,低29位全部为0的int,而wc则代表线程池中有效线程的数量workerCount,其为高3位全部为0,而低29位有值的int,将runState和workerCount做按位或,即用runState的高3位,workerCount的低29位填充的数字
构造函数
ThreadPoolExecutor一共有4个构造函数,但最后调用的都是如下构造函数
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;
}
参数 | 含义 |
---|---|
corePoolSize | 核心线程池大小 |
maximumPoolSize | 线程池最大容量大小 |
keepAliveTime | 线程池空闲时,除核心线程以外的线程存活的时间 |
TimeUnit | 线程活动保持时间的单位 |
BlockingQueue<Runnable> | 任务队列,用于保存等待执行的任务的阻塞队列 |
ThreadFactory | 用于设置线程的工厂 |
RejectedExecutionHandler | 饱和策略 |
这里简单说一下corePoolSize和maximumPoolSize,可以进行如下类比学习,corePoolSize=公司的基本人员,maximumPoolSize=公司的基本人员+外包人员。corePoolSize,保持存活的工作线程的最小数目,当小于corePoolSize时,会直接启动新的一个线程来处理任务,而不管线程池中是否有空闲线程
RejectedExecutionHandler是一个接口,有4个实现类,对应4种处理策略,这4个实现类是ThreadPoolExecutor的静态内部类
类 | 策略 |
---|---|
AbortPolicy | 丢弃任务,抛运行时异常 |
CallerRunsPolicy | 执行任务 |
DiscardPolicy | 忽视,什么都不会发生 |
DiscardOldestPolicy | 从队列中踢出最先进入队列(最后一个执行)的任务 |
execute执行流程
- 首先线程池判断基本线程池是否已满?没满,创建一个工作线程来执行任务。满了,则进入下个流程。
- 其次线程池判断工作队列是否已满?没满,则将新提交的任务存储在工作队列里。满了,则进入下个流程。
- 最后线程池判断整个线程池是否已满(即线程数是否小于线程池最大容量)?没满,则创建一个新的工作线程来执行任务,满了,则交给饱和策略来处理这个任务。
retry使用
break retry 跳到retry处,且不再进入循环
continue retry 跳到retry处,且再次进入循环
public static void main(String[] args) {
breakRetry();
continueRetry();
}
private static void breakRetry() {
int i = 0;
retry:
for (; ; ) {
System.out.println("start");
for (; ; ) {
i++;
if (i == 4)
break retry;
}
}
//start 进入外层循环
//4
System.out.println(i);
}
private static void continueRetry() {
int i = 0;
retry:
for(;;) {
System.out.println("start");
for(;;) {
i++;
if (i == 3)
continue retry;
System.out.println("end");
if (i == 4)
break retry;
}
}
//start 第一次进入外层循环
//end i=1输出
//end i=2输出
//start 再次进入外层循环
//end i=4输出
//4 最后输出
System.out.println(i);
}
submit和execute的联系和区别
execute这个方法在Executor接口中定义,并且直接由ThreadPoolExecutor来实现
public interface Executor {
void execute(Runnable command);
}
参考博客
Java线程池的分析和使用
[1]http://ifeve.com/java-threadpool/
[2]https://www.jianshu.com/p/ade771d2c9c0
[3]https://blog.csdn.net/lipeng_bigdata/article/details/51232266
[4]https://blog.csdn.net/qq_19431333/article/details/59030892
四种Java线程池用法解析
[5]https://www.cnblogs.com/ruiati/p/6134131.html