JAVA线程池
线程池类
Executor接口
其中就只有一个函数
void execute(Runnable command);
接口是一种规范,那么这个接口,就是要求其实现类,能够处理Runable的实例,换句话说,就是管理线程
ExecutorService
public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
ExecutorService继承了Executor,也是与线程池关系相当密切的一个接口,它一共定义了11个方法 ,加上execute一共12个方法
比起Execute,它不仅可以接收Runnable,还能接收Callable接口
这些方法起什么作用等下结合其实现类分析
Executors
这个类中有一些静态的工程方法和内部类,用于生成线程池级相关类
四个线程池实现类
java中常用的线程池有四个
CachedThreadPool
FixedThreadPool
ScheduledThreadPool
SingleThreadExecutor
通常,要创造他们的实例,都会使用Executors中的工厂方法
ExecutorService executor1 = Executors.newCachedThreadPool();
ExecutorService executor2 = Executors.newFixedThreadPool(8);
ExecutorService executor3 = Executors.newScheduledThreadPool(5);
ExecutorService executor4 = Executors.newSingleThreadExecutor();
拿到他们的类名称看看,究竟他们的实例属于哪个类
class java.util.concurrent.ThreadPoolExecutor
class java.util.concurrent.ThreadPoolExecutor
class java.util.concurrent.ScheduledThreadPoolExecutor
class java.util.concurrent.Executors$FinalizableDelegatedExecutorService
锁定目标之后,接下来就是逐个分析了
线程池实现
1.ThreadPoolExecutor
继承树:
上面的类信息表明,CachedThreadPool和FixedThreadPool实际上是属于同一个类,不同的应该只有创建时的参数不同而已
为了描述的简洁,我们跳过中间的查找过程,直接看ThreadPoolExecutor的最重要的构造器
七参构造器
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
构造器的方法体里面就是把这些值赋给类属性
可以看到这里一共7个参数,他们分别是:
int corePoolSize:线程池最小(核心)的线程数,如果allowCoreThreadTimeOut被标志为true,那线程数也可能小于这个数
注意这里的最小并不是要求线程数全程都大于等于corePoolSize,一开始是线程一个一个增加,到了corePoolSize这个数后,即使线程不用了,线程数也不会小于corePoolSize
int maximumPoolSize:线程池最大线程数
long keepAliveTime:线程数量大于corePoolSize时,多余线程的最大空闲存活时间,这个数字代表什么由下一个参数决定
TimeUnit unit:上一个参数的单位,TimeUnit是一个枚举类,建议查看源码
BlockingQueue
ThreadFactory threadFactory:线程工厂,用于创建线程
RejectedExecutionHandler handler:拒绝策略,用于线程池拒绝新的任务加入
除了这个构造器外,ThreadPoolExecutor还有一个五个参数的构造器
五(六)参构造器
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory //五参构造器没有这个参数
)
实际上,其内部也是调用了七个参数的构造器
而后面的两个实参是:
Executors.defaultThreadFactory()//五参构造器的默认值
defaultHandler
那么默认的defaultThreadFactory是什么样子的呢?
DefaultThreadFactory
Executors中的一个静态类,就是默认的线程工厂,除了构造器,之有一个方法:
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
简单来说,逻辑就是,这个线程不能是守护线程,默认的优先级是默认值5
defaultHandler
defaultHandler的实例是AbortPolicy这个类的实例
它的拒绝策略是直接抛出异常
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
相信大家早有耳闻,线程池一共四种拒绝策略:
这里我们就见到了第一种
FixedThreadPool的创建
这两个都是Executors中的工厂方法
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
FixedThreadPool:固定大小的线程池,其核心线程数量和最大核心数量都是nThreads,可以传入自定义的线程工厂,阻塞队列用的是LinkedBlockingQueue
CachedThreadPool的创建
这两个都是Executors中的工厂方法
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
CachedThreadPool:可缓存线程池,核心线程数为0,最大线程数为int的最大值,最大线程空闲存活时间是60秒,可以传入自定义的线程工厂,,阻塞队列用的是SynchronousQueue
FixedThreadPool和CachedThreadPool的区别
这两种线程池甚至使用了相同的构造器,所以显然只有构造器参数上的差别
数值的差别很好理解,但是为什么阻塞队列会有差别呢?
瞎JB乱猜开始了!
/**
因为我们可以认为,CachedThreadPool可以同时运行的线程数实际上是无限的
而阻塞队列存放的又是等待的任务
对于CachedThreadPool来说,任务自然不需要等待,有任务来,就会立马有线程接手
**/
2.ScheduledThreadPoolExecutor
定时的线程池
这个线程池的构造器参数和ThreadPoolExecutor完全一样,这里就不多说了
值得注意的是,默认的阻塞队列是DelayedWorkQueue,最大线程数是int最大值,而创建ScheduledThreadPoolExecutor需要指定的是corePoolSize,可选指定threadFactory
这个类的具体实现肯定与上一个有所不同,但是本博客还没到阅读源码的水平,等下实践给出例子体会一下应该就差不多了
3.FinalizableDelegatedExecutorService
这是Executor的内部类,其实就是一个ThreadPoolExecutor的代理类,只开放了部分方法,比如invokeall就没有开放
还重写了finalize方法,这是我以第一次见到有人这么干的
protected void finalize() {
super.shutdown();
}
工厂方法是这么写的
public static ExecutorService newSingleThreadExecutor() {
return new Executors.FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
4.其他线程池
上网搜索一下,java线程池的种类,大部分博客都会告诉你是4种,就是上面的4位
不知道这是那个版本jdk的说法了,实际上,在jdk8里面,Executors包含的工厂类就不止这4种.
单线程的延迟线程池,它也是一个被Executor内部类代理的线程池,核心是ScheduledThreadPoolExecutor,核心线程数为1,最大线程池为int最大值,阻塞队列是LinkedBlockingQueue
public static ScheduledExecutorService newSingleThreadScheduledExecutor();
这个线程池我就不了解是什么东西了,它实际上是一个ForkJoinPool,这些以后再去了解吧
public static ExecutorService newWorkStealingPool()
现在的线程池一共6种了,然而有些线程池仅仅以为构建的方式不同就分为不同的线程池,这种分类不便于我们理解
实际上,我们已知的线程池就是3种
ThreadPoolExecutor
ScheduledThreadPoolExecutor
ForkJoinPool
创建线程池不一定非要使用Executor,直接调用这些线程池的构造方法也可以
如果你够强,完全可以写一个自定义的线程池呀
补充
我们刚刚提到过一些知识点,但是还没有看清全貌
以下内容包含ThreadPoolExecutor和ScheduledThreadPoolExecutor,对于ForkJoinPool暂不分析
1.拒绝策略
拒绝策略就是线程池已经不能够接收新的任务时,对于新加入任务采取的措施
AbortPolicy
抛出异常
CallerRunsPolicy
由申请加入任务的线程自己执行
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
DiscardPolicy
直接丢弃,实际上就是什么都不做
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {}
DiscardOldestPolicy
丢弃最老的
从这里看也不一定是最老的呀,这阻塞队列可不一定是先进先出的
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
2.线程工厂
DefaultThreadFactory
默认的线程工厂,生成了一个非守护线程,优先级为5的线程
PrivilegedThreadFactory
这个类目前在我能力范围之外,大概与类加载器有关
总结
1.线程池种类
一般分类:
CachedThreadPool
FixedThreadPool
ScheduledThreadPool
SingleThreadExecutor
实际分类
ThreadPoolExecutor
ScheduledThreadPoolExecutor
ForkJoinPool
2.线程池参数(不包含ForkJoinPool)
int corePoolSize
int maximumPoolSize
long keepAliveTime
TimeUnit unit
BlockingQueue
ThreadFactory threadFactory
RejectedExecutionHandler handler
3.拒绝策略
AbortPolicy:抛异常
CallerRunsPolicy:交给原线程执行
DiscardPolicy:直接丢弃
DiscardOldestPolicy:丢弃最老的
部分方法
以下结论由测试得出,可能与事实有偏差
invokeAny
执行给定Callable集合中的某一个
FixedThreadPool总是执行第一个
CachedThreadPool大概率执行第一个
ScheduledThreadPool总是执行第一个
SingleThreadExecutor总是执行第一个
源码分析
以上总结了线程池是什么,下面分析一下具体的执行源码
成员变量
ctl
private final AtomicInteger ctl
这是一个表示状态的整数,高三位表示线程池运行状态,所谓的状态有:RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED。初始为RUNNING
后面所有未表示任务数量,不等同于线程数。
如有侵权,联系删除
2290713181@qq.com