Java线程池
线程复用
线程的创建和销毁,都是重量级工作,非常的消耗性能,高并发或者频繁的线程使用场景下对机器的要求很高,会造成性能瓶颈
可以使用线程池优化线程的频繁使用场景,使线程达到重复使用而不销毁,原理类似数据库连接池,http连接池等…
线程池概念
在线程池中,当你需要使用线程时,可以从池子中随便拿一个空闲线程,当完成工作时,并不急着关闭线程,而是将这个线程退回到线程池中
相当于创建线程变成了从线程池中获取线程
关闭线程变成了将线程退回给线程池
线程池工作原理
线程池定义:
- 核心线程数
- 最大线程数
- 保持空闲最大时长
- 工作队列
- 拒绝策略
- 线程池中原本没有线程,当任务队列中有任务的时候,才通过线程工厂创建线程,创建好的线程在线程池中为空闲状态,空闲状态的线程会以自旋的方式不断的从任务队列中取出任务执行,如果线程正在执行任务中,则为活跃状态,任务执行结束后又会回到空闲状态
- 线程工厂不会一直创建线程,当线程达到了线程池的最大线程数时就不会再创建线程
- 创建出来的线程不会一直存在于线程池中,如果线程池中的线程数量 > 核心线程数,则线程池会销毁处于空闲状态并持续了[保持空闲最大时长]的线程
- 任务队列中的一个任务被线程获取执行了则会从队列中移除
- 如果线程池中的所有线程都处于活跃中,则来不及处理的任务就会被堆积在任务队列中
- 当任务队列无法再存储更多任务的时候,这些无法存储的任务就会执行拒绝策略
ThreadPoolExecutor原理
源码原理
ThreadPoolExecutor
构造器
/**
* 创建一个线程池
*
* @param corePoolSize 核心线程数
* @param maximumPoolSize 最大线程数
* @param keepAliveTime 保持空闲最大时长
* @param unit 时间单位
* @param workQueue 工作队列,阻塞队列
* @param threadFactory 线程工厂
* @param handler 拒绝执行的策略
*/
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
常见的实现
拒绝策略
任务阻塞队列
如何实现线程复用
如何实现Runable
被执行完成后线程不会结束
答案是ThreadPoolExecutor.Worker
public class ThreadPoolExecutor extends AbstractExecutorService {
// ...忽略其他代码
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
// ...忽略其他代码
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;
/** Initial task to run. Possibly null. */
Runnable firstTask;
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
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 {
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
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);
}
}
}
}
可以看到,实际线程池在创建线程的时候,线程中执行的Runnable
并不是我们的Runnable
任务
线程池自己实现了一个Runnable
,用于绑定一个线程,并持续的在线程中自旋持续不断的从任务队列中获取出我们的Runnable
并执行一次,这就是为什么线程在执行完我们的Runnable
任务后,线程仍然存在并且没有关闭的原因,因为线程执行的实际上是Worker
,Worker
一直没有停止工作,是Woeker
在执行我们的Runnable
任务
newCachedThreadPool
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
缓存线程池newCachedThreadPool
实际上并没有核心线程数,所以其实是将所有的线程都缓存60秒,就类似给Redis
的key设置过期时间为60s一样,在线程空闲60秒后就会被销毁