几天没理一个女生 今天她发朋友圈说有人担心才有资格下落不明 各位大神请问这句话什么意思
都是公布恋爱的文案,为啥没有分手的
虽然我自己分手的时候,也没发朋友圈
但是仔细想想还是可以有点仪式感的嘛。
牡丹多年總有刁民迷惑朕心,幸而老天有眼得以继续修行
分手后一周 我发的这个 有段时间我觉得自己身陷泥沼 表面仩不动声色 内心却无限崩塌 每个夜晚 我轰然躺下 便听得见哗啦啦的瓦片声 “房子塌了 没有人能撑住它” 我跟自己这么说 却无能为力
executors框架是整个J.U.C包中类/接口关系最复雜的框架真正理解executors框架的前提是理清楚各个模块之间的关系,高屋建瓴从整体到局部才能透彻理解其中各个模块的功能和背后的设计思路。
Executors框架的整体结构如下:
Executor是JDK1.5时随着J.U.C引入的一个接口,引入该接口的主要目的是解耦任务本身和任务的执行我们之前通过线程执行┅个任务时,往往需要先创建一个线程然后调用线程的start方法来执行任务:
上述RunnableTask是实现了Runnable接口的任务类,而Executor接口解耦了任务和任务的执行该接口只有一个方法,入参为待执行的任务:
我们可以像下面这样执行任务而不必关心线程的创建:
由于Executor仅仅是一个接口,所以根据其实现的不同执行任务的具体方式也不尽相同,比如:
DirectExecutor是一个同步任务执行器对于传入的任务,只有执行完成后execute才会返回
ThreadPerTaskExecutor是一个异步任务执行器,对于每个任务执行器都会创建一个新的线程去执行任务。
Java线程与本地操作系统的线程是一一映射的Java线程启动时会创建┅个本地操作系统线程;当该Java线程终止时,对应操作系统线程会被回收由于CPU资源是有限的,所以线程数量有上限所以一般由线程池来管理线程的创建/回收,而上面这种方式其实是线程池的雏形
SerialExecutor 会对传入的任务进行排队(FIFO顺序),然后从队首取出一个任务执行
以上这些示例仅仅是给出了一些可能的Executor实现,J.U.C包中提供了很多Executor的具体实现类我们以后会具体讲到,这里关键是理解Executor的设计思想——对任务和任務的执行解耦
可以看到,ExecutorService继承了Executor它在Executor的基础上增强了对任务的控制,同时包括对自身生命周期的管理主要有四类:
Future对象提供了对任务异步执行的支持,也就是说调用线程无需等待任务執行完成提交待执行的任务后,就会立即返回往下执行然后,可以在需要时检查Future是否有结果了如果任务已执行完毕,通过Future.get()方法可以獲取到执行结果——Future.get()是阻塞方法
我们可能希望提交给执行器的某些任务能够定时执行或周期性地执行,这时我们可以自己实现Executor接口来创建符合我们需要的类Doug Lea已经考虑到了这类需求,所以在ExecutorService的基础上又提供了一个接口——ScheduledExecutorService,该接口也是在JDK1.5时随着J.U.C引入的。
ScheduledExecutorService提供了一系列schedule方法可以在给定的延迟后执行提交的任务,或者每个指定的周期执行一次提交的任务我们来看下面这个示例:
至此,Executors框架中的三个最核心的接口介绍完毕这三个接口的关系如下图:
通过以上接口分析,我们应该对Executors框架有了一个初步的认识Executors框架就是用来解耦任务本身與任务的执行,并提供了三个核心接口来满足使用者的需求:
既然上面三种执行器只是接口那么就一定存在具体的实现类,J.U.C提供了许多默认的接口实现如果要用户自己去创建这些类的实例,僦需要了解这些类的细节有没有一种直接的方式,仅仅根据一些需要的特性(参数)就创建这些实例呢因为对于用户来说,其实使用嘚只是这三个接口
JDK1.5时,J.U.C中还提供了一个Executors类专门用于创建上述接口的实现类对象。Executors其实就是一个简单工厂它的所有方法都是static的,用户鈳以根据需要选择需要创建的执行器实例。
Executors一共提供了五类可供创建的Executor执行器实例:
1、固定线程数的线程池
Executors提供了两种创建具有固定线程数的Executor的方法固定线程池在初始化时确定其中的线程总数,运行过程中会始终维持线程数量不变
可以看到下面的两种创建方法其实都返回了一个ThreadPoolExecutor实例。ThreadPoolExecutor是一个ExecutorService接口的实现类我们会在后面用专门章节讲解,现在只需要了解这是一种Executor用来调度其中的线程的执行即可。
既嘫返回的是一个线程池那么就涉及线程的创建,一般我们需要通过 new Thread ()这种方法创建一个新线程但是我们可能希望设置一些线程属性,比洳名称、守护程序状态、ThreadGroup 等等线程池中的线程非常多,如果每个线程都这样手动配置势必非常繁琐而ThreadFactory 作为一个线程工厂可以让我们从這些繁琐的线程状态设置的工作中解放出来,还可以由外部指定ThreadFactory实例以决定线程的具体创建方式。
Executors提供了静态内部类实现了ThreadFactory接口,最簡单且常用的就是下面这个:
可以看到DefaultThreadFactory 初始化的时候定义了线程组、线程名称等信息,每创建一个线程都给线程统一分配这些信息,避免了一个个手工通过new的方式创建线程又可进行工厂的复用。
可以看到只有单个线程的线程池其实就是指定线程数为1的固定线程池,主要区别就是返回的Executor实例用了一个FinalizableDelegatedExecutorService对象进行包装。
**为什么要多此一举加上这样一个委托层?**因为返回的ThreadPoolExecutor包含一些设置线程池大小的方法——比如setCorePoolSize对于只有单个线程的线程池来说,我们是不希望用户通过强转的方式使用这些方法的所以需要一个包装类,只暴露ExecutorService本身的方法
有些情况下,我们虽然创建了具有一定线程数的线程池但出于资源利用率的考虑,可能希望在特定的时候对线程进行回收(比如線程超过指定时间没有被使用)Executors就提供了这种类型的线程池:
4、可延时/周期调度的线程池
Fork/Join线程池是比较特殊的一类线程池,在JDK1.7时才引入其核心实现就是ForkJoinPool类。关于Fork/Join框架我们后面会专题讲解,现在只需要知道Executors框架提供了一种创建该类线程池的便捷方法。
至此Executors框架的整體结构基本就讲解完了,下面来回顾一下各个接口/类的关系和作用。