什么是线程池
在Java中,创建一个线程可以通过继承Thread或者实现Runnable接口来实现,但是,如果每个请求都创建一个新线程,那么创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。
为了解决这个问题,就有了线程池的概念,线程池的核心逻辑是提前创建好若干个线程放在一个容器种。如果有任务需要处理,则将任务直接分配给线程池中的线程来执行就行,任务处理完以后这个线程不会被销毁,而是等待后续分配任务。同时通过线程池来重复管理线程还可以避免创建大量线程增加开销。
创建线程池
为了方便使用,Java中的Executors类里面提供了几个线程池的工厂方法,可以直接利用提供的方法创建不同类型的线程池:
- newFixedThreadPool:创建一个固定线程数的线程池
- newSingleThreadExecutor:创建只有1个线程的线程池
- newCachedThreadPool:返回一个可根据实际情况调整线程个数的线程池,不限制最大线程 数量,若用空闲的线程则执行任务,若无任务则不创建线程。并且每一个空闲线程会在60秒 后自动回收。
- newScheduledThreadPool: 创建一个可以指定线程的数量的线程池,但是这个线程池还带有 延迟和周期性执行任务的功能,类似定时器。
FixedThreadPool
创建一个固定数量N个线程在一个共享的无边界队列上操作的线程池。在任何时候,最多N个线程被激活处理任务。如果所有线程都在活动状态时又有新的任务被提交,那么新提交的任务会加入队列等待直到有线程可用为止。
如果有任何线程在shutdown前因为失败而被终止,那么当有新的任务需要执行时会产生一个新的线程,新的线程将会一直存在线程池中,直到被显示的shutdown。
示例
package com.zwx.concurrent.threadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestThreadPool {
public static void main(String[] args) {
//FixedThreadPool - 固定线程数
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i=0;i<10;i++){
fixedThreadPool.execute(()-> {
System.out.println("线程名:" + Thread.currentThread().getName());
});
}
fixedThreadPool.shutdown();
}
}
输出结果为:
可以看到,最多只有3个线程在循环执行任务(运行结果是不一定的,但是最多只会有3个线程)。
FixedThreadPool调用了如下方法构造线程池:
SingleThreadExecutor
只有一个工作线程的执行器。如果这个线程在正常关闭前因为执行失败而被关闭,那么就会重新创建一个新的线程加入执行器。
这种执行器可以保证所有的任务按顺序执行,并且在任何给定的时间内,确保活动的任务只有1个。
示例
package com.zwx.concurrent.threadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestThreadPool {
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); for (int i=0;i<9;i++){
singleThreadExecutor.execute(()-> { System.out.println("线程名:" + Thread.currentThread().getName());
}); } }}singleThreadExecutor.shutdown();
运行结果只有1个线程:
SingleThreadExecutor调用了如下方法构造线程池:
CachedThreadPool
一个在需要处理任务时才会创建线程的线程池,如果一个线程处理完任务了还没有被回收,那么线程可以被重复使用。
当我们调用execute方法时,如果之前创建的线程有空闲可用的,则会复用之前创建好的线程,否则就会创建新的线程加入到线程池中。
创建好的线程如果在60s内没被使用,那么线程就会被终止并移出缓存。因此,这种线程池可以保持长时间空闲状态而不会消耗任何资源。
示例
package com.zwx.concurrent.threadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestThreadPool {
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); for (int i=0;i<9;i++){
cachedThreadPool.execute(()-> { System.out.println("线程名:" + Thread.currentThread().getName());
}); } cachedThreadPool.shutdown();}
输出结果可以看到,创建了9个不同的线程:
接下来我们对上面的示例改造一下,在执行execute之前休眠一段时间:
package com.zwx.concurrent.thre