1、什么是线程池
程序的世界里有各种池如:数据库连接池、HTTP连接池以及我们现在要说的这个线程池。池的作用很明显,就是用来存放一些比较重的资源,像获取数据库连接、http连接、开启新的线程,这些资源的获取及关闭都会耗费大量的内存及时间,对系统的性能会产生比较大的影响。
池化的作用便是对这些资源进行统一的管理,如线程池的管理:当有多个并发请求时,开启多个线程同时进行处理,当线程中的任务处理结束时,不是马上销毁线程,而是让其继续进行调度,当满足一定条件时才销毁。这样,当有新的任务进入,不必重新开启新的线程,直接从池子里取出之前的线程用于本次任务处理,提高了任务的处理速度并减少系统因频繁开启和关闭线程浪费系统资源。
2、J.U.C中的线程池
2.1 ThreadPoolExecutor
先来看构造函数:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
属性 | 说明 |
---|---|
corePoolSize | 核心线程大小,最小工作线程数 |
maximumPoolSize | 最大开启线程大小,当达到最大线程大小,线程池不会再开启新的线程,新的任务将会加入到任务队列等待处理 |
keepAliveTime | 当开启的线程数大于corePoolSize时,空闲的线程最大的存活时间 |
unit | 存活时间的单位,可以是秒、分、时、天等 |
workQueue | 工作队列,线程来不及处理的任务就是先放在这里排队 |
上面的表格对该线程池的重要参数做了简要说明,Executors提供的静态方法其实就是一个对常用的线程池参数进行简单的封装:
方法 | 说明 |
---|---|
newFixedThreadPool(int nThreads) | new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue()) ,固定大小的线程池 |
newSingleThreadExecutor() | new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue())) ,只有一个线程的线程池 |
newCachedThreadPool() | new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue()) ,缓存的线程池 |
newFixedThreadPool(10)
:固定大小为10的线程池,可以看到核心线程数=最大线程数,意味着线程池中的线程不会被回收(详见keepAliveTime字段说明),每次有新任务提交到线程池,都会开启一个新的线程,知道线程数已经达到最大的10个线程。然后该线程池会一直存在10个线程,不管有没有处理任务。
newSingleThreadExecutor
:只有一个线程的线程池,和newFixedThreadPool方法类似。可以看到这个线程池还用FinalizableDelegatedExecutorService这个类封装后再返回,原因是因为只有一个线程,不需要线程监控的功能。
newFixedThreadPool和newSingleThreadExecutor
两种线程池都是用LinkedBlockingQueue阻塞队列,这个队列是一个链表类型的队列,当任务处理不及时时会一直插入队列的尾部,可能会造成任务的积压。
newCachedThreadPool
:缓存的线程池能够将空闲的线程缓存60s(见构造函数),开启的最大的线程数为Integer.MAX_VALUE,再看其队列的类型为SynchronousQueue
,这个队列是同步队列,里面只能存放一个任务,当任务处理很慢且存在很多任务时,该线程池可能来不及处理,导致一直开启新的线程,直到达到最大值,发生这种情况对系统是一种灾难!
综上,我们在使用ThreadPoolExecutor这个线程池的时候必须根据业务的特点:
1、使用其构造函数构造自己需要的线程池,设置合理的核心线程和最大线程,以及keepAliveTime和队列类型。
2、将业务分为核心业务和普通业务2个或多个线程池,避免非核心业务对核心业务的处理造成影响。
2.2 ForkJoinPool
【小家java】Java线程池之—ForkJoinPool线程池的使用以及原理
3、自己动手写一个支持动态拓展的线程池
参考ThreadPoolExecutor(java.util.concurrent.ThreadPoolExecutor#addWorker、java.util.concurrent.ThreadPoolExecutor#runWorker、java.util.concurrent.ThreadPoolExecutor#getTask、java.util.concurrent.ThreadPoolExecutor#tryTerminate),实现一个简单版的可拓展线程池。原理:
1、构造函数参数支持核心线程数、最大线程数、空闲线程存活时间,队列使用LinkedList链表(为了模拟线程池动态增减,设置了最大排队10个任务)
2、每次提交任务到线程池,根据核心和最大线程选择开启新的线程或加入到任务队列
3、每次任务处理结束后,轮询从队列中取任务,当轮询时间超过keepAliveTime,尝试关闭线程池中空闲线程。(与ThreadPoolExecutor不同,ThreadPoolExecutor采用的是workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
,详见java.util.concurrent.ThreadPoolExecutor#getTask
方法)
完整代码如下:
public class SimpleThreadPoolExecutor implements Executor {
private volatile int corePoolSize;
private volatile int maximumPoolSize;
private volatile long keepAliveTime;
private final ReentrantLock takeLock = new ReentrantLock();
private final LinkedList<Runnable> workQueue;
private final int maxQueueSize = 10; // 队列最大排队任务数
private final HashSet<Worker> workers = new HashSet<Worker>();
private ReentrantLock reentrantLock = new ReentrantLock();
private ThreadFactory threadFactory;
/**
* 当前线程池大小
*/
private volatile int currentPoolSize = 0;
public SimpleThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime) {
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.keepAliveTime = keepAliveTime;
this.workQueue = new LinkedList<>();
this.threadFactory = new DefaultThreadFactory();
}
public int getCurrentPoolSize() {
return currentPoolSize;
}
public HashSet<Worker> getWorkers() {
return workers;
}
public int getQueueSize() {
return workQueue.size();
}
public class Worker implements Runnable {
Runnable firstTask;
private final Thread thread;
private final ReentrantLock lock = new ReentrantLock();
/**
* 线程是否空闲
*/
private volatile boolean isIdle = false;
public Worker(Runnable firstTask) {
this.firstTask = firstTask;
this.thread = threadFactory.newThread(this);
}
public boolean isIdle() {
return isIdle;
}
@Override
public void run() {
runWorker(this);
}
private void lock() {
lock.lock();
}
private void unlock() {
lock.unlock();
}
}
@Override
public void execute(Runnable command) {
if (currentPoolSize < corePoolSize) {
addWorker(command);
}
final ReentrantLock lock = this.reentrantLock;
lock.lock();
try {
if (workers.size() > 0 && workQueue.size() <= maxQueueSize) {
workQueue.addLast(command);
return;
}
} finally {
lock.unlock();
}
if (currentPoolSize < maximumPoolSize) {
addWorker(command);
} else if (!addWorker(command)) {
lock.lock();
try {
workQueue.addLast(command);
} finally {
lock.unlock();
}
}
}
private boolean addWorker(Runnable task) {
final ReentrantLock lock = this.reentrantLock;
lock.lock();
Worker worker = new Worker(task);
try {
if (currentPoolSize + 1 > maximumPoolSize) {
return false;
}
this.workers.add(worker);
currentPoolSize++;
} finally {
lock.unlock();
}
Thread t = worker.thread;
if (t != null) {
t.start();
}
return true;
}
private void runWorker(Worker worker) {
Runnable task = worker.firstTask;
long elapsedTime = 0;
long eachSleepTime = 10;
try {
for (; ; ) {
while (task != null || (task = getTask()) != null) {
worker.lock();
try {
task.run();
worker.isIdle = false;
task = null;
} finally {
worker.unlock();
}
}
// 休眠一会
sleep(eachSleepTime);
if ((task = getTask()) != null) {
// 重新开始计算空闲时间
elapsedTime = 0;
continue;
}
elapsedTime += eachSleepTime;
if (elapsedTime >= keepAliveTime * 1000) {
worker.lock();
worker.isIdle = true;
worker.unlock();
break;
}
}
} finally {
closeIdleThread();
}
}
/**
* 关闭空闲线程
*/
private void closeIdleThread() {
reentrantLock.lock();
try {
if (currentPoolSize > corePoolSize) {
Iterator<Worker> $it = workers.iterator();
while ($it.hasNext()) {
Worker worker = $it.next();
worker.lock();
try {
if (worker.isIdle) {
worker.thread.interrupt();
$it.remove();
currentPoolSize--;
}
} finally {
worker.unlock();
}
}
}
} finally {
reentrantLock.unlock();
}
}
/**
* 从队列中取出任务
*
* @return
*/
private Runnable getTask() {
takeLock.lock();
try {
Runnable task = workQueue.removeFirst();
return task;
} catch (Exception e) {
//
return null;
} finally {
takeLock.unlock();
}
}
/**
* 线程工厂:默认创建守护线程
*/
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
t.setDaemon(true);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
private void sleep(long milliSeconds) {
try {
TimeUnit.MILLISECONDS.sleep(milliSeconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试类:
public class ThreadPoolClient {
public static void main(String[] args) throws IOException {
SimpleThreadPoolExecutor executor = new SimpleThreadPoolExecutor(1, 6, 5L);
AtomicInteger seq = new AtomicInteger(0);
for(int i = 0; i < 20; i++) {
executor.execute(() -> {
sleep(1000);
System.out.println("hello" + seq.getAndIncrement());
});
}
new Thread(() -> {
while (true) {
sleep(1000);
System.out.println(String.format("CurrentPoolSize->%s, QueueSize->%s", executor.getCurrentPoolSize(), executor.getQueueSize()));
}
}).start();
sleep(10000);
System.out.println("继续提交任务");
for(int i = 0; i < 20; i++) {
executor.execute(() -> {
sleep(1000);
System.out.println("hello" + seq.getAndIncrement());
});
}
System.in.read();
}
private static void sleep(long milliSeconds) {
try {
TimeUnit.MILLISECONDS.sleep(milliSeconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}