##1、线程池概念
线程池是线程的集合,通过线程池我们不需要自己创建线程,将任务提交给线程池即可。为什么要使用线程池,首先,使用线程池可以重复利用已有的线程继续执行任务,避免线程在创建和销毁时造成的消耗。其次,由于没有线程创建和销毁时的消耗,可以提高系统响应速度。最后,通过线程可以对线程进行合理的管理,根据系统的承受能力调整可运行线程数量的大小等。
##2、线程池使用示例
这里做两个测试,首先写一个实现Runnable接口的类:
class MyTask implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName());
}
}
main中代码:
ExecutorService pool = Executors.newFixedThreadPool(5);
Runnable myTask = new MyTask();
Runnable myTask1 = new MyTask();
Runnable myTask2 = new MyTask();
Runnable myTask3 = new MyTask();
Runnable myTask4 = new MyTask();
pool.execute(myTask);
pool.execute(myTask1);
pool.execute(myTask2);
pool.execute(myTask3);
pool.execute(myTask4);
这里newFixedThreadPool中的5代表可运行线程数量。所以结果:
pool-1-thread-3
pool-1-thread-1
pool-1-thread-4
pool-1-thread-5
pool-1-thread-2
可以看到这里又五个线程在运行。这里如果将可运行线程数量变成2,那么结果如下:
pool-1-thread-2
pool-1-thread-1
pool-1-thread-2
pool-1-thread-1
pool-1-thread-2
可以看到线程1和线程2都被重复利用了,没有创建更多的线程出来。
##3、线程池的状态
线程池的状态及状态间的切换如下图所示:
可以看到线程池有五种状态RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED,不同于线程的转台切换的是,线程池的状态可操作的其实只有两种状态,RUNNING和TERMINATED,线程池一旦创建就会处于RUNNING状态,而TERMINATED是终止,线程池调用shutdown或者shutdownNow线程池其实就会慢慢的切换到终止状态,中间三种状态只是线程由运行态过渡到终止态的中间状态,用来进行一些处理事项的。RUNNING状态是正常运行状态,能够接受新的任务和处理任务,SHUTDOWN是关闭状态,不能接受新的任务,将正在执行的任务处理完毕后进入到TIDYING整理状态,STOP是停止状态,不接受任务,也不处理任务,直接中断任务,然后进入到TIDYING整理状态,TIDYING状态会自动调用terminate方法,调用完后进入到终止状态。
##4、通过源码理解线程池
首先是线程的创建,线程的创建一般通过一个工厂类创建,
ExecutorService pool = Executors.newFixedThreadPool(2);
进去看这个函数:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
可以看到还是直接new了一个线程池出来,再看这个线程池的构造方法:
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:表示线程执行任务后的空闲存活时间,过了这个时间线程将被销毁,这个参数的存在也直接实现了概述中所说的前两点优点,线程的消耗可以分为A,B,C三个阶段,A代表创建,B代表执行,C代表消耗,如果有很多个任务需要执行时,B执行所带来的消耗代价小于A,C所带来的消耗代价,那么就通过这个参数,让线程保持一段存活时间,方便执行后面的任务。
unit:线程空闲存活时间的单位。
workQueue:这个是任务队列,将后面的任务加入到这个队列中。
threadFactory:真正用于创建线程的参数。
handler:表示已经不能在接收任务时,调用的处理类。
在这个构造方法中分别对这几个参数进行了初始化。**这里再介绍下这几个参数的关系,当用户创建任务时,如果当前的运行的线程数小于核心池数量,那么会创建新的线程来执行这个任务,如果当前运行的线程数大于核心池的数量,那么就将任务添加到任务队列中,如果任务队列已经装满且当前运行线程数小于最大池的数量,那么就再创建线程来执行这个任务,如果已经等于了最大池的数量,那么将拒绝这个任务并且执行handle处理类。**线程的创建介绍了再来看任务的执行方法execute:
public void execute(Runnable command) {
//当任务为空,抛出空指针异常
if (command == null)
throw new NullPointerException();
//这个是获取到ctl参数的值,这个值是32位的
//高三位存储了线程池的状态,其余位存储了线程池中运行的线程的数量
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)
addWorker(null, false);
}
//如果添加队列失败了,那么再创建一个新的线程
else if (!addWorker(command, false))
//如果失败了,也就是最大池满了,那么调用拒绝的处理方法。
reject(command);
}
这里多次调用到addWorker方法,这里继续看addWorker方法:
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 {
//获取可重入互斥锁
final ReentrantLock mainLock = this.mainLock;
//创建一个新的线程来之心这个任务
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
//线程不为空,加锁
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int c = ctl.get();
int rs = runStateOf(c);
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
//将worker添加到set集合中
workers.add(w);
//获取集合的worker数量
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
//worker添加成功执行任务
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
到这里线程池的入门介绍结束。