什么是线程池?
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。
线程池的好处:
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能执行
- 提高现场的可管理性。线程是稀缺资源。如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用线程池,必须对其实现原理了如指掌
线程池的执行流程:
1.线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下个流程。
2.线程池判断工作队列是否已满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里,如果工作队列满了,则进入下个流程。
3.线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。
如图:
线程池的工作原理代码实现:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//1.当前池中线程比核心数少,新建一个线程执行任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//2.核心池已满,但任务队列未满,添加到队列中
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);
}
//3.核心池已满,队列已满,试着创建一个新线程
else if (!addWorker(command, false))
reject(command); //如果创建新线程失败了,说明线程池被关闭或者线程池完全满了,拒绝任务
}
而饱和策略有以下四种:
- AbortPolicy:直接抛出异常
- CallerRunsPolicy:只用调用者所在线程来运行任务
- DiscardOldestPolicy:丢弃队列里最旧的一个任务,并执行当前任务
- DiscardPolicy:不处理,丢弃掉
当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或者持久化存储不能出炉的任务。
- keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以,如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率。
- TimeUnit(线程活动保持时间的 单位):可选的单位有天(DAYS)、小时(HOURS)、分钟(MINUTES)、毫秒(MILLISECONDS)、微妙(MICROSECONDS,千分之一毫秒)和纳秒(NANOSECONDS,千分之一微妙)。
线程池分为四种:
- newCachedThreadPool:创建一个可缓存线程池
- newFixedThreadPool:创建一个定长线程池
- newScheduledThreadPool:创建一个核心线程数固定,而非核心线程数不固定的线程池
- newSingleThreadExecutor:创建一个单线程化的线程池
详细介绍:
- newCachedThreadPool,是一种线程数量不定的线程池,并且其最大线程数为Integer.MAX_VALUE,这个数是很大的,一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。但是线程池中的空闲线程都有超时限制,这个超时时长是60秒,超过60秒闲置线程就会被回收。调用execute将重用以前构造的线程(如果线程可用)。这类线程池比较适合执行大量的耗时较少的任务,当整个线程池都处于闲置状态时,线程池中的线程都会超时被停止。
- newFixedThreadPool 创建一个指定工作线程数量的线程池,每当提交一个任务就创建一个工作线程,当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭了,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列(没有大小限制)中。由于newFixedThreadPool只有核心线程并且这些核心线程不会被回收,这样它更加快速底相应外界的请求。
- newScheduledThreadPool创建一个线程池,它的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收,它可安排给定延迟后运行命令或者定期地执行。这类线程池主要用于执行定时任务和具有固定周期的重复任务。
- newSingleThreadExecutor这类线程池内部只有一个核心线程,以无界队列方式来执行该线程,这使得这些任务之间不需要处理线程同步的问题,它确保所有的任务都在同一个线程中按顺序中执行,并且可以在任意给定的时间不会有多个线程是活动的。
代码演示:
public class ThreadPoolExecutorDemo {
//newCachedThreadPool
public static void CachedThreadPoolDemo() {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index * 1000);
} catch (InterruptedException e) {
e.printStackTrace(); }
cachedThreadPool.execute(new Runnable() {
public void run() {
System.out.println(index);
}
});
}
}
//newFixedThreadPool
public static void FixedThreadPoolDemo() {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
//newScheduledThreadPool
public static void SchedulThreadPoolDemo() {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {
public void run() {
System.out.println("delay 3 seconds");
}
}, 3, TimeUnit.SECONDS);
}
//newCachedThreadPool
public static void SingleThreadExecutorDemo() {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
System.out.println(Thread.currentThread().getName()+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
public static void main(String[] args) {
CachedThreadPoolDemo();
FixedThreadPoolDemo();
SchedulThreadPoolDemo();
SingleThreadExecutorDemo();
}
}
在使用线程池时,Java给我们提供了创建线程池的一个类Executor,而我们创建时,一般使用它的子类ThreadPoolExecutor
继承关系:
public class ThreadPoolExecutor extends AbstractExecutorService {}
ThreadPoolExecutor的几种构造参数:
- corePoolSize:核心线程的数量。默认是没有超时的,也就是说就算线程闲置,也不会被处理。但是如果设置了allowCoreTimeOut为true,那么当核心线程闲置时,会被回收。
- maximumPoolSize:最大线程池尺寸,被CAPACITY限制(2^29-1)。
- keepAliveTime:闲置线程被回收的时间限制
- unit:keepAliveTime的单位
- workQueue:用于存放任务的队列 四种 有界队列 无界队列 同步队列 优先级队列
- threadFactory:创建线程的工厂类
- handler:当任务执行失败时,使用handler通知调用者
合理使用线程池能够带来的好处:
1.降低资源消耗,减少创建和销毁线程的次数,每个工作线都可以被重复利用,可执行多个任务。
2.提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
3.提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为小号福哦多的内存,而把服务器累趴下(每个线程大概1MB内存,线程开得越多,消耗的内存就越大,最后死机)。