概念
线程池是创建一定数量的线程,等待任务的到来,在执行完任务之后,继续放在池子中继续等待。
优点
- 降低资源的消耗,节省线程创建和销毁的消耗。
- 提高响应速度,节省创建和销毁的时间
- 方便进行线程管理
简易线程池实现
这里实现了个非常简单的线程池,功能非常简单,仅仅为了方便理解线程池。
主要思路就是:1、建立一个线程池子,可以存放工作线程。
2、建立一个队列,可以存放需要运行的线程。
public class MyThreadPool {
/**
* 核心线程数
*/
private static int CORE_POOL_SIZE = 5;
/**
* 队列最大容量
*/
private static int MAX_QUEUE_SIZE = 100;
/**
* 队列
*/
private BlockingQueue<Runnable> taskQueue;
/**
* 线程数组
*/
private WorkThread[] workThreads;
/**
* 用户在构造这个池,希望的启动的线程数
*/
private int worker_num;
/**
* 构造函数,使用默认参数
* @param taskQueue
*/
public MyThreadPool(BlockingQueue<Runnable> taskQueue) {
this(CORE_POOL_SIZE, MAX_QUEUE_SIZE);
}
public MyThreadPool(int worker_num, int taskCount) {
this.worker_num = worker_num;
taskQueue = new ArrayBlockingQueue<>(taskCount);
workThreads = new WorkThread[worker_num];
for (int i = 0; i < worker_num; i++) {
workThreads[i] = new WorkThread();
workThreads[i].start();
}
}
/**
* 执行线程,其实就是将任务放入到队列中
* @param runnable
*/
public void execute(Runnable runnable) {
try {
taskQueue.put(runnable);
} catch (InterruptedException e) {
System.out.println("执行任务异常");
e.printStackTrace();
}
}
/**
* 销毁线程池
*/
public void destroy() {
for (int i = 0; i < workThreads.length; i++) {
WorkThread workThread = workThreads[i];
workThread.stopTask();
workThreads[i] = null;
}
taskQueue.clear();
}
/**
* 自定义工作线程
* 它的作用就是从队列中获取线程去执行以及停止任务
*/
class WorkThread extends Thread {
@Override
public void run() {
try {
while (!isInterrupted()) {
// 从队列中获取任务执行
Runnable task = taskQueue.take();
task.run();
System.out.println(getId()+" 执行 :"+task);
}
} catch (InterruptedException e) {
System.out.println("任务执行异常");
e.printStackTrace();
}
}
public void stopTask() {
interrupt();
}
}
}
// 测试类
public class MyThreadPoolTest {
public static void main(String[] args) throws InterruptedException {
MyThreadPool myThreadPool = new MyThreadPool(3, 10);
myThreadPool.execute(new WorkRunner("线程1"));
myThreadPool.execute(new WorkRunner("线程2"));
myThreadPool.execute(new WorkRunner("线程3"));
myThreadPool.execute(new WorkRunner("线程4"));
myThreadPool.execute(new WorkRunner("线程5"));
myThreadPool.execute(new WorkRunner("线程6"));
Thread.sleep(20000);
myThreadPool.destroy();
}
static class WorkRunner implements Runnable {
private String name;
public WorkRunner(String name) {
this.name = name;
}
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "任务执行完成");
}
}
}
测试结果
-
预定义的线程池
- FixedThreadPool
创建固定线程数量的线程池。使用了无界队列.
// 可以看到最大线程数和核心线程数相同
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- SingleThreadExecutor
创建单个线程。任务会顺序执行,使用了无界队列。
// 核心线程和最大线程都是1
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- CachedThreadPool
每来一个任务就会创建一个线程,适合很多短期异步任务,使用SynchronousQueue。SynchronousQueue
是一个内部只能包含一个元素的队列。插入元素到队列的线程被阻塞,直到另一个线程从队列中获取了队列中存储的元素。
// 核心线程是0,最大线程数是最大
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- ScheduledThreadPoolExecutor
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
需要定期执行周期任务。
newSingleThreadScheduledExecutor:只包含一个线程,只需要单个线程执行周期任务,保证顺序的执行各个任务
newScheduledThreadPool 可以包含多个线程的,线程执行周期任务,适度控制后台线程数量的时候
方法说明:
schedule:只执行一次,任务还可以延时执行
scheduleAtFixedRate:提交固定时间间隔的任务
scheduleWithFixedDelay:提交固定延时间隔执行的任务
/**
* 当执行时间大于间隔时间的情况。执行结果是
* ....begin:2019-08-02 23:40:03
* ....end:2019-08-02 23:40:11
* ....begin:2019-08-02 23:40:11
* ....end:2019-08-02 23:40:13
* ....begin:2019-08-02 23:40:15
* ....end:2019-08-02 23:40:20
*/
public static SimpleDateFormat formater = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
ScheduledThreadPoolExecutor schedule =
new ScheduledThreadPoolExecutor(1);
// initialDelay 初始化延迟 period间隔
schedule.scheduleAtFixedRate(new Demo(), 1000, 6000,
TimeUnit.MILLISECONDS);
}
static class Demo implements Runnable {
AtomicInteger atomicInteger = new AtomicInteger(0);
@Override
public void run() {
try {
if (atomicInteger.get() == 0) {
System.out.println("....begin:"+formater.format(new Date()));
Thread.sleep(8000);
atomicInteger.getAndIncrement();
System.out.println("....end:"+formater.format(new Date()));
} else if (atomicInteger.get() == 1) {
System.out.println("....begin:"+formater.format(new Date()));
Thread.sleep(2000);
atomicInteger.getAndIncrement();
System.out.println("....end:"+formater.format(new Date()));
} else if (atomicInteger.get() == 2) {
System.out.println("....begin:"+formater.format(new Date()));
Thread.sleep(5000);
atomicInteger.getAndIncrement();
System.out.println("....end:"+formater.format(new Date()));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- WorkStealingPool
jdk7以后新出的线程池。
-
源码解析
- 构造函数
如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;
如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;
如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;
如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。
* corePoolSize: 线程池核心线程数
*
maximumPoolSize:线程池最大数
*
keepAliveTime: 空闲线程存活时间。
*
unit: 时间单位
*
workQueue: 线程池所使用的缓冲队列
*
threadFactory:线程池创建线程使用的工厂,给新建的线程赋予名字
*
handler: 线程池对拒绝任务的处理策略
AbortPolicy :直接抛出异常,默认;
CallerRunsPolicy:用调用者所在的线程来执行任务
DiscardOldestPolicy:丢弃阻塞队列里最老的任务,队列里最靠前的任务
DiscardPolicy :当前任务直接丢弃
实现自己的饱和策略,实现RejectedExecutionHandler接口即可
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
- 全局变量
RUNNING:运行状态,该状态下线程池可以接受新的任务,也可以处理阻塞队列中的任务
SHUTDOWN:待关闭状态,不再接受新的任务,继续处理阻塞队列中的任务,当阻塞队列中的任务为空,并且工作线程数为0时,进入 TIDYING 状态
STOP:停止状态,不接收新任务,也不处理阻塞队列中的任务,并且会尝试结束执行中的任务,当工作线程数为0时,进入 TIDYING 状态
TIDYING:整理状态,此时任务都已经执行完毕,并且也没有工作线程,执行terminated方法后进入 TERMINATED 状态
TERMINATED:终止状态,此时线程池完全终止了,并完成了所有资源的释放
// 线程池状态(前三位)及线程个数(后二十九位)
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
// 线程容量 000 11111111111111111111111111111
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 运行中 111 00000000000000000000000000000
private static final int RUNNING = -1 << COUNT_BITS;
// 关闭 000 00000000000000000000000000000
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 停止 001 00000000000000000000000000000
private static final int STOP = 1 << COUNT_BITS;
// 整理 010 00000000000000000000000000000
private static final int TIDYING = 2 << COUNT_BITS;
// 终止 011 00000000000000000000000000000
private static final int TERMINATED = 3 << COUNT_BITS;
// 线程池状态(前三位)
private static int runStateOf(int c) {
return c & ~CAPACITY;
}
// 线程个数(后29位)
private static int workerCountOf(int c) {
return c & CAPACITY;
}
private static int ctlOf(int rs, int wc) {
return rs | wc;
}
-
submit
运行线程,有返回值。可以看到它其实调的也是execute方法
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
-
excute
运行线程,无返回值
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 如果线程数量小于核心线程数
if (workerCountOf(c) < corePoolSize) {
// 调用addWorker创建新线程并运行,成功直接返回
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)
// 如果线程数为0,则添加一个新的线程
addWorker(null, false);
}
// 如果新增新线程失败,则执行拒绝策略
else if (!addWorker(command, false))
reject(command);
}
addWorker创建新线程并运行
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (; ; ) {
int c = ctl.get();
int rs = runStateOf(c);
// 如果线程池状态为SHUTDOWN、STOP、TIDYING、TERMINATED
// 或者线程为空、阻塞队列为空则都返回false
if (rs >= SHUTDOWN &&
!(rs == SHUTDOWN &&
firstTask == null &&
!workQueue.isEmpty()))
return false;
for (; ; ) {
int wc = workerCountOf(c);
// 如果线程数量超过最大线程数或者大于核心线程数(自定义最大线程数)返回false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 增加线程数
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get();
// 如果线程池状态发生改变,则跳出内循环,重新判断线程池状态
if (runStateOf(c) != rs)
continue retry;
}
}
boolean workerStarted = false;
boolean workerAdded = false;
ThreadPoolExecutor.Worker w = null;
try {
w = new ThreadPoolExecutor.Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
// 加锁
mainLock.lock();
try {
int rs = runStateOf(ctl.get());
// 如果线程状态为运行中或者(状态为SHUTDOWN并且任务不为空)
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive())
throw new IllegalThreadStateException();
// 添加到工作线程集合中
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
// 如果word添加成功,则启动线程
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
// 线程没有启动成功,则将word从集合中移除,并且size-1
if (!workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
Worker里run方法所调用的方法
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock();
boolean completedAbruptly = true;
try {
// 如果线程不为空,并且从队列中成功获取到任务
while (task != null || (task = getTask()) != null) {
w.lock();
// 当线程池是处于STOP状态或者TIDYING、TERMINATED状态时,设置当前线程处于中断状态
// 如果不是,当前线程就处于RUNNING或者SHUTDOWN状态,确保当前线程不处于中断状态
// 重新检查当前线程池的状态是否大于等于STOP状态
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 {
// 已完成任务数+1,解锁
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
// 做一些售后工作,例如将线程从集合中移除、判断线程是否需要终止
// 判断队列是否为空,是否需要保留至少一个线程等工作
processWorkerExit(w, completedAbruptly);
}
}
getTask从队列中获取任务
private Runnable getTask() {
boolean timedOut = false;
for (; ; ) {
int c = ctl.get();
int rs = runStateOf(c);
// 如果线程状态非运行状态并且(状态是停止、整理、终止或者队列为空)
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
// 将线程数 -1
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 如果线程数量大于自定义最大线程数 并且 (线程数量>1或者阻塞队列为空),返回null
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 从队列中获取任务
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
-
关闭线程池
- shutdown
不再接收新的任务,只会中断所有没有执行任务的线程
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
- shutdownNow
不再接收新的任务,还会尝试停止正在运行或者暂停任务的线程
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
可以看到shutdown是将状态改为SHUTDOWN,而shutdownNow是将状态改为STOP,再返回去看getTask这个方法的这句,可以看到是不会拿到新的任务去运行了。
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
// 将线程数 -1
decrementWorkerCount();
return null;
}
再看runWorker这个方法的这句。可以看到当是STOP及以上状态时候,会让线程中断,而SHUTDOWN则不会。
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
计算密集型和IO密集型
- 计算密集型。指大部分时间都花费在了计算上,消耗cpu资源。
- IO密集型。指大部分时间花在网络、磁盘等交互上。
合理配置线程池
- 计算密集型线程任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低。所以线程数适当小一点,最大推荐:机器的Cpu核心数+1
- IO密集型。任务的大部分时间都在等待IO操作完成。对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。所以推荐线程数是机器的Cpu核心数*2