深入浅出JAVA线程池使用原理2

Posted Lucky帅小武

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入浅出JAVA线程池使用原理2相关的知识,希望对你有一定的参考价值。

一、Executor框架介绍

Executor框架将Java多线程程序分解成若干个任务,将这些任务分配给若干个线程来处理,并得到任务的结果

1.1、Executor框架组成

任务:被执行任务需要实现的接口:Runnable接口或Callable接口

任务的执行:任务执行的核心接口Executor以及其子类ExecutorService接口

任务的结果:包括Future接口以及Future接口的实现类FutureTask类

 

Executor接口是Executor框架的基础,将任务的提交与执行分离开

ThreadPoolExecutor:线程池核心实现类,用来执行提交的任务

ScheduledThreadPoolExecutor:在给定的延迟时间后执行或定期执行任务,相当于ThreadPoolExecutor的定时器版本

Future接口和实现类FutureTask代表异步处理的结果

Runnable接口和Callable接口都是当做任务被ThreadPoolExecutor和ScheduledThreadPoolExecutor来执行

 

二、ThreadPoolExecutor详解

ThreadPoolExecutor通常可以使用工厂类Executors来创建,可以有以下三种类型:

SingleThreadExecutor:单线程线程池执行器,适用于需要保证顺序地执行各个任务且不会出现多线程同时活动的情况

FixedThreadPool:创建使用固定线程数量的线程池,可以限制当前线程数量,比较常用。

CachedThreadPool:大小无界的线程池,适用于需要执行大量短期异步任务的场景或者是服务器负载较小的情况

 

2.1、FixedThreadPool详解

源码如下:

1 public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
2         return new ThreadPoolExecutor(nThreads, nThreads,
3                                       0L, TimeUnit.MILLISECONDS,
4                                       new LinkedBlockingQueue<Runnable>(),
5                                       threadFactory);
6     }

 

可以看出只需要设置两个参数:线程数量和线程工厂(可不传)

1.这里可以看出FixedThreadPool使用的工作队列是无界的链表结构的阻塞队列,所以这里的maximumPoolSize参数就设置成了和coolPoolSize一样的值,因为设置再大也失去了意义;

2.多余线程保持活跃的时间设置成了0,也就是多余的空闲线程会立即终止;

3.由于队列是无界的,所以线程池不会出现拒绝任务的情况,所以也不会用到饱和策略;

4.整体的工作流程就是:

线程池中线程数量少于corePoolSize时会先创建新线程来预热,达到之后就将任务放入LinkedBlockingQueue队列中,线程执行完任务之后会循环从队列中获取任务来执行

 

2.2、SingleThreadExecutor详解

源码如下:

1 public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
2         return new FinalizableDelegatedExecutorService
3             (new ThreadPoolExecutor(1, 1,
4                                     0L, TimeUnit.MILLISECONDS,
5                                     new LinkedBlockingQueue<Runnable>(),
6                                     threadFactory));
7     }

 

可以看出SingleThreadExecutor和FixedThreadPool几乎一样,只是线程只会有一个线程,相比于FixedThreadPool的好处就是可以保证任务的顺序性

2.3、CachedThreadPool详解

源码如下:

1 public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
2         return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
3                                       60L, TimeUnit.SECONDS,
4                                       new SynchronousQueue<Runnable>(),
5                                       threadFactory);
6     }

 

可以看出也只需要设置线程工厂一个参数,而其他参数都是固定的,corePoolSize设置为0,即corePool为空;maximumPoolSize设置为了Integer的最大值就表示是无大小限制了。keepAliveTime设置成了60秒;队列使用了SynchronousQueue

整体的工作流程为:

1.提交任务到SynchronousQueue队列中,如果线程池中有空余的线程在执行poll操作,则此任务就会被该线程获取执行

2.如果线程池中没有空闲线程,则就会在线程池中创建新的线程来执行poll操作获取任务

3.线程池中的空闲线程如果poll操作没有获取到任务,则会等待60秒,如果没有等待任务则线程终止

所以CachedThreadPool的好处是没有任务的时候,线程池中不需要创建线程等待任务,或者任务执行完之后,线程会在不久就会释放,节省了资源。但是如果任务数量较大,且线程处理任务的速度慢于主线程提交任务的速度,那么线程池中就会不停的创建新线程来执行,

就会有线程数量过多而导致CPU和内存资源被消耗尽的情况

 

三、FutureTask详解

3.1、FutureTask用法

FutureTask除了实现了Future接口,还实现了Runnable接口,所以FutureTask可以被Executor执行,也可以直接调用本身的run方法执行。

FutureTask有三种状态:未启动(未执行run方法)、已启动(执行run方法中)、已结束(run方法结束或抛出异常)

当FutureTask不是处于已结束状态时,调用get方法会阻塞当前线程知道任务结束;当处于已结束状态时,调用get方法会返回任务执行的结果或抛出对应的异常

当FutureTask处于未启动状态时,调用cancel方法会导致该任务永远不会被执行;

当FutureTask处于已启动状态时,调用cancel(true)方法会中断执行的任务,调用cancel(false)方法将不会对正在执行此任务的线程产生影响

当FutureTask处于已结束状态时,调用cancel方法会返回false

 

3.2、FutureTask实现原理

FutureTask底层是基于AQS(抽象同步队列)实现的

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

以上是关于深入浅出JAVA线程池使用原理2的主要内容,如果未能解决你的问题,请参考以下文章

深入浅出JAVA线程池使用原理1

深入源码,深度解析Java 线程池的实现原理

多线程——Java线程池原理深入

Java线程池详解

深入源码,深度解析Java 线程池的实现原理

深入分析java线程池的实现原理