线程池基本学习
为什么使用线程池?
经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
线程池思路
提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁、实现重复利用。
线程池的好处
1.提高响应速度(减少了创建新线程的时间)
2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
3.便于线程管理
Executor、ExecutorService接口 以及 ThreadPoolExecutor类
Java定义了Executor接口并在该接口中定义了一个execute()方法用于执行一个线程任务。
public interface Executor {
void execute(Runnable command);//执行任务、命令,没有返回值,一般用来执行Runnable
}
然后通过ExecutorService接口实现Executor接口并执行具体的线程操作,所以ExecutorService才是真正的线程池接口。
public interface ExecutorService extends Executor {
...
<T> Future<T> submit(Callable<T> task);//执行任务,有返回值,一般又来执行Callable
List<Runnable> shutdownNow();//关闭连接池
...
}
而ThreadPoolExecutor类 是我们真正使用的线程池类,它有四个构造方法,如下图:
可以直接看最后一个构造方法(原因:参数最多,最全面)
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
参数顺序 | 名称 | 类型 | 含义 |
---|---|---|---|
1 | corePoolSize | int | 核心线程池大小 |
2 | maximumPoolSize | int | 最大线程池大小(前提是不能超过最大线程数量,否则该任务只能进入阻塞队列进行排队等候,直到有线程空闲了,才能继续执行任务) |
3 | keepAliveTime | long | 表示线程存活时间,除了核心线程外,那些被新创建出来的线程可以存活多久。意味着,这些新的线程一但完成任务,而后面都是空闲状态时,就会在一定时间后被摧毁。 |
4 | unit | TimeUnit | 存活时间单位 |
5 | workQueue | BlockingQueue |
表示任务的阻塞队列,由于任务可能会有很多,而线程就那么几个,所以那么还未被执行的任务就进入队列中排队,队列我们知道是 FIFO 的,等到线程空闲了,就以这种方式取出任务。这个一般不需要我们去实现。 |
6 | threadFactory | ThreadFactory | 线程创建工厂 |
7 | handler | RejectedExecutionHandler | 拒绝策略(当任务太多来不及处理时,如何拒绝任务) |
Executors 类是什么?
public class Executors extends Object
Executors是用来为Executor、ExecutorService 、ScheduledExecutorService、ThreadFactory、Callable类提供了一些工具方法。说白了Executors就是一个工具类、线程池的工厂类,用于创建并返回不同类型的线程池。
常用的5种线程池
1.newFixedThreadPool (固定大小的线程池)
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
corePoolSize与maximumPoolSize相等都为nThreads,即其线程全为核心线程,就是一个固定大小的线程池并且任务执行是无序的;
keepAliveTime = 0 该参数默认对核心线程无效,而FixedThreadPool全部为核心线程;
workQueue 为LinkedBlockingQueue(*阻塞队列),队列最大值为Integer.MAX_VALUE。如果任务提交速度持续大余任务处理速度,会造成队列大量阻塞。因为队列很大,很有可能在拒绝策略前,内存溢出。
// 案例代码
public class FixThreadPool {
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
Fix fix = new Fix();
fixedThreadPool.execute(fix);
fixedThreadPool.execute(fix);
fixedThreadPool.execute(fix);
fixedThreadPool.execute(fix);
fixedThreadPool.execute(fix);
fixedThreadPool.execute(fix);
fixedThreadPool.shutdownNow();
}
}
class Fix implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程启动");
}
}
/* 运行结果如下:
pool-1-thread-2线程启动
pool-1-thread-3线程启动
pool-1-thread-1线程启动
可以看出,大小确实被固定了只有3个线程运行,而且执行顺序是无序的
*/
2.newSingleThreadExecutor (单个线程的线程池)
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
仔细看newSingleThreadExecutor()方法,其实就是 固定大小为 1 的线程池并通过FinalizableDelegatedExecutorService类包装了一次
//案例代码
public class SingleThreadExecutor {
public static void main(String[] args) {
ExecutorService fixedExecutorService = Executors.newFixedThreadPool(1);
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) fixedExecutorService;
System.out.println(threadPoolExecutor.getMaximumPoolSize());
threadPoolExecutor.setMaximumPoolSize(2);
System.out.println(threadPoolExecutor.getMaximumPoolSize());
/* 运行结果如下:
1
2
可以看出 FixedThreadPool类可以向下转型为ThreadPoolExecutor类,并重新对其线程池进行配置
*/
/*
fixedExecutorService2被FinalizableDelegatedExecutorService类包装后,无法成功向下转型。因此,fixedExecutorService2被定以后,无法修改,做到了真正的Single
以下语句会在 运行时异常 java.lang.ClassCastException:
*/
ExecutorService fixedExecutorService2 = Executors.newSingleThreadExecutor();
ThreadPoolExecutor threadPoolExecutor2 = (ThreadPoolExecutor) fixedExecutorService2;
}
}
3.newCachedThreadPool (可缓存的线程池)
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
corePoolSize = 0,maximumPoolSize = Integer.MAX_VALUE,即线程数量几乎无限制;
keepAliveTime = 60s,线程空闲60s后自动结束。
workQueue 为 SynchronousQueue 同步队列,这个队列入队出队必须同时传递,因为CachedThreadPool线程创建无限制,不会有队列等待,所以使用SynchronousQueue;
4.newScheduledThreadPool(可做任务调度的线程池)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);//1.点进去
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());//2.点进去
}
//3.终点
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
可以看出 newScheduledThreadPool调用的是ScheduledThreadPoolExecutor的构造方法,而ScheduledThreadPoolExecutor继承了ThreadPoolExecutor,构造是还是调用了其父类的构造方法。
newScheduledThreadPool 创建了一个可定时调度的线程池,可设置在给定的延迟时间后执行或者定期执行某个线程任务。
// 案例代码
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println("延迟3秒执行");
}
}, 3, TimeUnit.SECONDS);
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("延迟3秒执行,并且1秒执行一次");
}
}, 3, 1, TimeUnit.SECONDS);
5.newWorkStealingPool (足够大小的线程池)
public static ExecutorService newWorkStealingPool() {//点进入
return new ForkJoinPool(Runtime.getRuntime().availableProcessors(), ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
//终点
public ForkJoinPool(int parallelism,
ForkJoinWorkerThreadFactory factory,
UncaughtExceptionHandler handler,
boolean asyncMode) {
this(checkParallelism(parallelism),
checkFactory(factory),
handler,
asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
"ForkJoinPool-" + nextPoolId() + "-worker-");
checkPermission();
}
newWorkStealingPool 是 JDK1.8 版本加入的一种线程池,它实现的一个线程池和上面4种都不一样,用的是 ForkJoinPool 类,它是一个并行的线程池,参数中传入的是一个线程并发的数量,这里和之前就有很明显的区别,前面4种线程池都有核心线程数、最大线程数等等,而这就使用了一个并发线程数解决问题,而且这个线程池也不会保证任务的顺序执行。