线程池
在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁,这就是”池化资源”技术产生的原因。线程池顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。
1. 我们先说一些简单使用
要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,因此在工具类Executors面提供了一些静态工厂方法,生成一些常用的线程池,如下所示:
我们先来看简单的使用也就是使用工具类Executors来创建常用线程池
- newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
- newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
- newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
- newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
如果希望在服务器上使用线程池,建议使用newFixedThreadPool方法来创建线程池,这样能获得更好的性能。
1. newSingleThreadExecutor()
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
我们可以观察到,控制台的打印输出的线程名都是一样的。
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
for(int i = 0; i < 100; i++){
System.out.println(Thread.currentThread().getName()+"="+i);
}
}
});
}
}
2. newFixedThreadPool(100);
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(100);
for (int i = 0; i < 10; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
for(int i = 0; i < 100; i++){
System.out.println(Thread.currentThread().getName()+"="+i);
}
}
});
}
}
3. newCachedThreadPool()
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
for(int i = 0; i < 100; i++){
System.out.println(Thread.currentThread().getName()+"="+i);
}
}
});
}
}
4. newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行。
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
for (int i = 0; i < 10; i++) {
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
for(int i = 0; i < 100; i++){
System.out.println(Thread.currentThread().getName()+"="+i);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},3, TimeUnit.SECONDS);
}
}
打印输出,每隔三秒输出一次,每次都输出另外一个线程2121212121
pool-1-thread-2=0
pool-1-thread-1=0
pool-1-thread-2=1
pool-1-thread-1=1
pool-1-thread-1=2
pool-1-thread-2=2
pool-1-thread-1=3
pool-1-thread-2=3
pool-1-thread-1=4
pool-1-thread-2=4
。。。
2. Java 5+中的Executor接口定义一个执行线程的工具。它的子类型即线程池接口是ExecutorService。
我们稍微看一下上面四个创建的时候底层都发生了什么,我们可以看到他们其实本质都是创建了一个ThreadPoolExecutor对象,并返回一个ExecutorService ,只不过是参数不同,我们接下来说这些参数的含义
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>());
}
1. 我们首先找到这个构造器
public ThreadPoolExecutor(int corePoolSize,//线程中的核心线程数
int maximumPoolSize,//线程池中最大的工作线程的数量,当corePoolSize与队列都满的情况下,就允许继续创建新的工作线程,
long keepAliveTime,//闲置秒数,超过会被回收
TimeUnit unit,
BlockingQueue<Runnable> workQueue,//阻塞中的队列,如果线程池中运行的线程达到了corePoolSize,就会进入队列中
ThreadFactory threadFactory,
RejectedExecutionHandler handler) //如果maximumPoolSize与队列都满了,就会拒绝{
}
1 corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程。
2 workQueue(任务队列) : 用于保存等待执行的任务的阻塞队列。可以选择以下几个阻塞队列:
ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,按FIFO原则进行排序
LinkedBlockingQueue:一个基于链表结构的阻塞队列,吞吐量高于ArrayBlockingQueue。静态工厂方法Excutors.newFixedThreadPool()使用了这个队列
SynchronousQueue: 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量高于LinkedBlockingQueue,静态工厂方法Excutors.newCachedThreadPool()使用了这个队列
PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
3 maximumPoolSize(线程池最大数量):线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是,如果使用了无界的任务队列这个参数就没用了。
4 threadFactory(线程工厂):可以通过线程工厂为每个创建出来的线程设置更有意义的名字,如开源框架guava
5 RejectedExecutionHandler (饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略还处理新提交的任务。它可以有如下四个选项:
AbortPolicy:直接抛出异常,默认情况下采用这种策略
CallerRunsPolicy:只用调用者所在线程来运行任务
DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务
DiscardPolicy:不处理,丢弃掉
更多的时候,我们应该通过实现RejectedExecutionHandler 接口来自定义策略,比如记录日志或持久化存储等。
6 keepAliveTime(线程活动时间):线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程利用率。
7 TimeUnit(线程活动时间的单位):可选的单位有天(Days)、小时(HOURS)、分钟(MINUTES)、毫秒(MILLISECONDS)、微秒(MICROSECONDS,千分之一毫秒)和纳秒(NANOSECONDS,千分之一微秒)。
运行流程:

2. 接下来我们来看重要的执行线程的方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
//ctl是什么:我们查看发现他是一个
//private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); 详情查看2.1
//获取线程池的状态和其中的线程和数量
int c = ctl.get();
//===============线程池的线程数量小于corePoolSize核心线程数量,开启核心线程执行任务。===============//
//如果线程的数量小于corePoolSize,则添加线程执行任务
if (workerCountOf(c) < corePoolSize) {
//添加线程,修改线程数量,并将command线程作为第一个任务处理,详情见2.2
if (addWorker(command, true))
return;
c = ctl.get();
}
//===============线程池的线程数量不小于corePoolSize核心线程数量,
或者开启核心线程失败,尝试将任务以非阻塞的方式添加到任务队列。===============//
//如果线程池的状态是RUNNING,将线程添加到队列中
if (isRunning(c) && workQueue.offer(command)) { //private final BlockingQueue<Runnable> workQueue;
//二次检查线程池状态
int recheck = ctl.get();
//如果已经停了,就移除掉要运行的线程
if (! isRunning(recheck) && remove(command))
//拒绝掉任务
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//===============任务队列已满导致添加任务失败,开启新的非核心线程执行任务。===============//
else if (!addWorker(command, false))
reject(command);
}
2. 1--ctlOf(RUNNING, 0)
我们看到
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
是获取到一个原子的整型对象,参数是通过ctlOf(RUNNING, 0)获取的
- 接下来我们看一下这个方法:返回这两个值或后的结果,
private static int ctlOf(int rs, int wc) { return rs | wc; }
- 我们接下来看这个RUNNING是什么,它实际代表的意思为当前线程池的状态
实际上是-1左移(32-3)位
private static final int RUNNING = -1 << COUNT_BITS;
//我们可以自己运行一下
System.out.println(Integer.toBinaryString (-1 << Integer.SIZE - 3));
11100000000000000000000000000000
对于ctl来说:
-
workerCountOf()方法取得当前线程池的线程数量,算法是将ctl的值取低29位。
-
runStateOf()方法取得线程池的状态,算法是将ctl的值取高3位:
-
RUNNING 111 表示正在运行
-
SHUTDOWN 000 表示拒绝接收新的任务
-
STOP 001 表示拒绝接收新的任务并且不再处理任务队列中剩余的任务,并且中断正在执行的任务。
-
TIDYING 010 表示所有线程已停止,准备执行terminated()方法。
-
TERMINATED 011 表示已执行完terminated()方法。
3. ctl结论:
那么我们上面的ctl代表的意思就为
高3位代表线程池的状态,低29位代表的是线程池的数量
默认是RUNNING状态, 线程池的数量为0
2. 2--addWorker(command, true)
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
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))
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 {
//创建一个Worker对象,那么Worker又是什么呢?,我们看下面的2. 2.1
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
//添加独占锁,为添lworker进行同步操作,防止其他线程同时进行execute方法。
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//获取到线程池的状态
int rs = runStateOf(ctl.get());
//如果线程池状态为RUNNING或者是线程池状态为SHUTDOWN并且第一个任务为空时,
// 当线程池状态,为SHUTDOWN时,是不允许添加新任务的,所以他会从队列中获取任务。
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
//添加worker到集合
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
//添加成功
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
//如果添加worker成功,就启动任务,见2. 2.2看start调用的run方法是怎么运行的
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
2. 2.1--new Worker(firstTask);
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
//执行任务的线程,通过ThreadFactory创建
final Thread thread;
//初始化第一个任务
Runnable firstTask;
//每个线程完成任务的数量
volatile long completedTasks;
线程池本身是一个HashSet
private final HashSetworkers = new HashSet ();
2. 2.2--t.start();
public void run() {
runWorker(this);
}
---
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
/*循环进行获取任务,如果第一个任务不为空,或者是如果第一个任务为空,从任务队列中获取任务,
如果有任务则返回获取的任务信息,如果没有任务可以获取则进行阻塞,阻塞也分两种,第一种是
阻塞直到任务队列中有内容,第二种是阻塞队列一定时间之后还是没有任务就直接返回nu11。*/
while (task != null || (task = getTask()) != null) {
w.lock();
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
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 {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
未完结
博客参照:
https://www.cnblogs.com/zhaoyan001/p/7049627.html
https://blog.csdn.net/jackfrued/article/details/44921941 ,第65题

浙公网安备 33010602011771号