线程池
什么是线程池?
线程池就是创建若干个可执行线程放入一个池(容器)中,有任务需要执行时,就从线程池中获取一个线程用来执行,使用完毕放回线程池中。
JVM在HotSpot的线程模型下,Java线程会一对一映射为内核线程,也就说Java中每创建/回收一个线程都会去内核创建/回收,涉及到内核操作的都是很消耗资源的,这就可能导致创建/回收线程所消耗的资源比处理任务所消耗的资源更多,为了提高资源的利用率,就引入了线程池的概念。
这样提高了资源的利用率,提高了线程的可复用性
三大方法
newSingleThreadExecutor
源码
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));//容量为Integer.MAX_VALUE
}
使用
ExecutorService threadPool = Executors.newSingleThreadExecutor();//创建只有一个线程的线程池
for (int i = 0; i < 10; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
threadPool.shutdown();//线程池使用完毕必须要手动关闭
结果
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
newFixedThreadPool
源码
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());//容量为Integer.MAX_VALUE
}
使用
ExecutorService threadPool = Executors.newFixedThreadPool(2);//创建固定数量的线程池
for (int i = 0; i < 10; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
threadPool.shutdown();
结果
pool-1-thread-2 ok
pool-1-thread-1 ok
pool-1-thread-2 ok
pool-1-thread-1 ok
pool-1-thread-2 ok
pool-1-thread-1 ok
pool-1-thread-2 ok
pool-1-thread-2 ok
pool-1-thread-2 ok
pool-1-thread-1 ok
newCachedThreadPool
源码
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(
0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
使用
ExecutorService threadPool = Executors.newCachedThreadPool();//创建可伸缩的线程池
for (int i = 0; i < 100; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
threadPool.shutdown();
结果
pool-1-thread-2 ok
pool-1-thread-6 ok
pool-1-thread-5 ok
pool-1-thread-1 ok
pool-1-thread-4 ok
pool-1-thread-3 ok
pool-1-thread-9 ok
pool-1-thread-8 ok
pool-1-thread-7 ok
pool-1-thread-10 ok
七大参数
- corePoolSize:核心线程数定义了最小可以同时运行的线程数量
- maximumPoolSize:当队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数
- keepAliveTime:当线程池中的线程数量大于corePoolSize的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime才会被回收销毁、
- unit:keepAliveTime参数的世界单位
- workQueue:当新任务来的时候会先判断当前运行的线程数量是否达到了核心线程数,如果达到了的话,新任务就会被存放在队列中
- threadFactory:executor创建新线程的时候会用到
- handler:饱和策略。
四大策略
- ThreadPoolExecutor.AbortPolicy:抛出RejectedExecutionException来拒绝新任务的处理
- ThreadPoolExecutor.DiscardPolicy:不处理新任务,直接丢弃掉
- ThreadPoolExecutor.DiscardOldestPolicy:丢弃最早加入队列的任务,尝试将新任务加入队列中
- ThreadPoolExecutor.CallerRunsPolicy:由调用者处理该任务。调用者线程既要提交新任务又要忙于执行任务,会影响程序的整体性能。如果你的应用程序可以承受此延迟并且你不能丢弃任何一个任务请求的话,你可以选择这个策略。
工作流程
1、有新的任务来临时,当前线程数量不足corePoolSize时,会由executor指定的TheradFactory创建一个新的线程来执行任务
2、有新的任务来临时,当前线程数量达到corePoolSize时,workQueue未满时,会将任务加入workQueue中,等待执行
3、有新的任务来临时,当前线程数量到达了corePoolSize且workQueue队列已满,则会判断当前线程数量是否小于maximumPoolSize,如果满足就由executor指定的TheradFactory新建一个线程来执行任务
4、如果大于maximumPoolSize由拒绝策略处理
5、如果当先线程数量大于corePoolSize,只要有线程空闲的时间超过keepAliveTime指定的时间,就会将这个线程回收
CPU密集型和IO密集型
-
CPU密集型:以计算为主的程序。以计算为主的程序线程数最好和CPU核心数相同(N),可以减少CPU切换带来的消耗,以获得最大执行效率。
-
IO密集型:以input/output为主的程序。由于IO的速度远不及CPU的速度,CPU在处理IO时往往伴随着等待,为获得最大的执行效率一般将程序的线程数设置为IO任务的2倍(2N)
线程池生命周期
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3; //高3位用来表示线程状态
private static final int CAPACITY = (1 << COUNT_BITS) - 1;//低29位用来表示线程数量// =000 11111...
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS; // 111 00000...
private static final int SHUTDOWN = 0 << COUNT_BITS; // 000 00000...
private static final int STOP = 1 << COUNT_BITS; // 001 00000...
private static final int TIDYING = 2 << COUNT_BITS; // 010 00000...
private static final int TERMINATED = 3 << COUNT_BITS; // 011 00000...
// 线程池的状态
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 线程池中工作线程的数量
private static int workerCountOf(int c) { return c & CAPACITY; }
// 计算ctl的值,等于运行状态“加上”线程数量
private static int ctlOf(int rs, int wc) { return rs | wc; }
作者巧妙的使用Integer的前三位表示线程的状态,后29位来表示工作线程数量。也就是说线程数量最多只能有2^29-1
- RUNNING:当new一个线程池时会进入此状态,表示线程池处于一个运行状态,和线程的生命周期无关
- SHUTDOWN:处于RUNNING的线程,调用shutdown()方法进去此状态,此状态下不会再新的任务,会把当前正在运行的任务以及workQueue中的任务执行完
- STOP:处于RUNNING/SHUTDOWN的线程,调用shutdownNow()方法进入此状态,此状态不会在执行任务,正在执行的任务会抛出一个中断异常
- TIDYING:所有任务已中止,且工作线程数量为0时,会进去此状态,此状态会进行资源的回收与整理
- TERMINATED:处于TIDYING状态的线程,当整理完毕后调用terminated()方法进入此状态,也就是线程池结束