instances = createSpringFactoriesInstances(type, parameterTypes,
223 | classLoader, args, names);
224 | // 【4】进行排序
225 | AnnotationAwareOrderComparator.sort(instances);
226 | // 【5】返回加载并实例化好的接口实现类
227 | return instances;
228 | }
229 | ```
230 |
231 | 可以看到,SpringBoot自定义实现的SPI机制代码中最重要的是上面代码的`【1】`,`【2】`,`【3】`步,这3步下面分别进行重点分析。
232 |
233 | # 4.1 获得类加载器
234 |
235 | 还记得[Java是如何实现自己的SPI机制的?](https://juejin.im/post/5e7c26a76fb9a009a441757c)这篇文章中Java的SPI机制默认是利用线程上下文类加载器去加载扩展类的,那么,**SpringBoot自己实现的SPI机制又是利用哪种类加载器去加载`spring.factories`配置文件中的扩展实现类呢?**
236 |
237 | 我们直接看第`【1】`步的`ClassLoader classLoader = getClassLoader();`这句代码,先睹为快:
238 |
239 | ```java
240 | // SpringApplication.java
241 |
242 | public ClassLoader getClassLoader() {
243 | // 前面在构造SpringApplicaiton对象时,传入的resourceLoader参数是null,因此不会执行if语句里面的逻辑
244 | if (this.resourceLoader != null) {
245 | return this.resourceLoader.getClassLoader();
246 | }
247 | // 获取默认的类加载器
248 | return ClassUtils.getDefaultClassLoader();
249 | }
250 | ```
251 |
252 | 继续跟进`getDefaultClassLoader`方法:
253 |
254 | ```java
255 | // ClassUtils.java
256 |
257 | public static ClassLoader getDefaultClassLoader() {
258 | ClassLoader cl = null;
259 | try {
260 | // 【重点】获取线程上下文类加载器
261 | cl = Thread.currentThread().getContextClassLoader();
262 | }
263 | catch (Throwable ex) {
264 | // Cannot access thread context ClassLoader - falling back...
265 | }
266 | // 这里的逻辑不会执行
267 | if (cl == null) {
268 | // No thread context class loader -> use class loader of this class.
269 | cl = ClassUtils.class.getClassLoader();
270 | if (cl == null) {
271 | // getClassLoader() returning null indicates the bootstrap ClassLoader
272 | try {
273 | cl = ClassLoader.getSystemClassLoader();
274 | }
275 | catch (Throwable ex) {
276 | // Cannot access system ClassLoader - oh well, maybe the caller can live with null...
277 | }
278 | }
279 | }
280 | // 返回刚才获取的线程上下文类加载器
281 | return cl;
282 | }
283 | ```
284 |
285 | 可以看到,原来SpringBoot的SPI机制中也是用线程上下文类加载器去加载`spring.factories`文件中的扩展实现类的!
286 |
287 | # 4.2 加载spring.factories配置文件中的SPI扩展类
288 |
289 | 我们再来看下第`【2】`步中的`SpringFactoriesLoader.loadFactoryNames(type, classLoader)`这句代码是如何加载`spring.factories`配置文件中的SPI扩展类的?
290 |
291 | ```java
292 | // SpringFactoriesLoader.java
293 |
294 | public static List loadFactoryNames(Class> factoryClass, @Nullable ClassLoader classLoader) {
295 | // factoryClass即SPI接口,比如ApplicationContextInitializer,EnableAutoConfiguration等接口
296 | String factoryClassName = factoryClass.getName();
297 | // 【主线,重点关注】继续调用loadSpringFactories方法加载SPI扩展类
298 | return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
299 | }
300 | ```
301 |
302 | 继续跟进`loadSpringFactories`方法:
303 |
304 | ```java
305 | // SpringFactoriesLoader.java
306 |
307 | /**
308 | * The location to look for factories.
309 | * Can be present in multiple JAR files.
310 | */
311 | public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
312 |
313 | private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
314 | // 以classLoader作为键先从缓存中取,若能取到则直接返回
315 | MultiValueMap result = cache.get(classLoader);
316 | if (result != null) {
317 | return result;
318 | }
319 | // 若缓存中无记录,则去spring.factories配置文件中获取
320 | try {
321 | // 这里加载所有jar包中包含"MATF-INF/spring.factories"文件的url路径
322 | Enumeration urls = (classLoader != null ?
323 | classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
324 | ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
325 | result = new LinkedMultiValueMap<>();
326 | // 遍历urls路径,将所有spring.factories文件的键值对(key:SPI接口类名 value:SPI扩展类名)
327 | // 加载放到 result集合中
328 | while (urls.hasMoreElements()) {
329 | // 取出一条url
330 | URL url = urls.nextElement();
331 | // 将url封装到UrlResource对象中
332 | UrlResource resource = new UrlResource(url);
333 | // 利用PropertiesLoaderUtils的loadProperties方法将spring.factories文件键值对内容加载进Properties对象中
334 | Properties properties = PropertiesLoaderUtils.loadProperties(resource);
335 | // 遍历刚加载的键值对properties对象
336 | for (Map.Entry, ?> entry : properties.entrySet()) {
337 | // 取出SPI接口名
338 | String factoryClassName = ((String) entry.getKey()).trim();
339 | // 遍历SPI接口名对应的实现类即SPI扩展类
340 | for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
341 | // SPI接口名作为key,SPI扩展类作为value放入result中
342 | result.add(factoryClassName, factoryName.trim());
343 | }
344 | }
345 | }
346 | // 以classLoader作为key,result作为value放入cache缓存
347 | cache.put(classLoader, result);
348 | // 最终返回result对象
349 | return result;
350 | }
351 | catch (IOException ex) {
352 | throw new IllegalArgumentException("Unable to load factories from location [" +
353 | FACTORIES_RESOURCE_LOCATION + "]", ex);
354 | }
355 | }
356 | ```
357 |
358 | 如上代码,`loadSpringFactories`方法主要做的事情就是利用之前获取的线程上下文类加载器将`classpath`中的所有`spring.factories`配置文件中所有SPI接口的所有扩展实现类给加载出来,然后放入缓存中。**注意**,这里是一次性加载所有的SPI扩展实现类哈,所以之后根据SPI接口就直接从缓存中获取SPI扩展类了,就不用再次去`spring.factories`配置文件中获取SPI接口对应的扩展实现类了。比如之后的获取`ApplicationListener`,`FailureAnalyzer`和`EnableAutoConfiguration`接口的扩展实现类都直接从缓存中获取即可。
359 |
360 | > **思考1:** 这里为啥要一次性从`spring.factories`配置文件中获取所有的扩展类放入缓存中呢?而不是每次都是根据SPI接口去`spring.factories`配置文件中获取呢?
361 |
362 | > **思考2:** 还记得之前讲的SpringBoot的自动配置源码时提到的`AutoConfigurationImportFilter`这个接口的作用吗?现在我们应该能更清楚的理解这个接口的作用了吧。
363 |
364 |
365 | 将所有的SPI扩展实现类加载出来后,此时再调用`getOrDefault(factoryClassName, Collections.emptyList())`方法根据SPI接口名去筛选当前对应的扩展实现类,比如这里传入的`factoryClassName`参数名为`ApplicationContextInitializer`接口,那么这个接口将会作为`key`从刚才缓存数据中取出`ApplicationContextInitializer`接口对应的SPI扩展实现类。其中从`spring.factories`中获取的`ApplicationContextInitializer`接口对应的所有SPI扩展实现类如下图所示:
366 |
367 | 
368 |
369 | # 4.3 实例化从spring.factories中加载的SPI扩展类
370 |
371 | 前面从`spring.factories`中获取到`ApplicationContextInitializer`接口对应的所有SPI扩展实现类后,此时会将这些SPI扩展类进行实例化。
372 |
373 | 此时我们再来看下前面的第`【3】`步的实例化代码:
374 | `List instances = createSpringFactoriesInstances(type, parameterTypes,
375 | classLoader, args, names);`。
376 |
377 | ```java
378 | // SpringApplication.java
379 |
380 | private List createSpringFactoriesInstances(Class type,
381 | Class>[] parameterTypes, ClassLoader classLoader, Object[] args,
382 | Set names) {
383 | // 新建instances集合,用于存储稍后实例化后的SPI扩展类对象
384 | List instances = new ArrayList<>(names.size());
385 | // 遍历name集合,names集合存储了所有SPI扩展类的全限定名
386 | for (String name : names) {
387 | try {
388 | // 根据全限定名利用反射加载类
389 | Class> instanceClass = ClassUtils.forName(name, classLoader);
390 | // 断言刚才加载的SPI扩展类是否属于SPI接口类型
391 | Assert.isAssignable(type, instanceClass);
392 | // 获得SPI扩展类的构造器
393 | Constructor> constructor = instanceClass
394 | .getDeclaredConstructor(parameterTypes);
395 | // 实例化SPI扩展类
396 | T instance = (T) BeanUtils.instantiateClass(constructor, args);
397 | // 添加进instances集合
398 | instances.add(instance);
399 | }
400 | catch (Throwable ex) {
401 | throw new IllegalArgumentException(
402 | "Cannot instantiate " + type + " : " + name, ex);
403 | }
404 | }
405 | // 返回
406 | return instances;
407 | }
408 | ```
409 |
410 | 上面代码很简单,主要做的事情就是实例化SPI扩展类。
411 | 好了,SpringBoot自定义的SPI机制就已经分析完了。
412 |
413 | > **思考3:** SpringBoot为何弃用Java的SPI而自定义了一套SPI?
414 |
415 | # 5 小结
416 |
417 | 好了,本片就到此结束了,先将前面的知识点再总结下:
418 |
419 | 1. 分析了`SpringApplication`对象的构造过程;
420 | 2. 分析了SpringBoot自己实现的一套SPI机制。
421 |
422 | # 6 有感而发
423 |
424 | 从自己2月开始写源码分析文章以来,也认识了一些技术大牛,从他们身上看到,越厉害的人越努力。回想一下,自己现在知识面也很窄,更重要的是对自己所涉及的技术没有深度,一句话概括,还很菜,而看到比自己厉害的大牛们都还那么拼,自己有啥理由不努力呢?很喜欢丁威老师的一句话:"**唯有坚持不懈**"。然后自己一步一个脚印,相信自己能取得更大的进步,继续加油。
425 |
426 |
427 |
428 | **原创不易,帮忙Star一下呗**!
429 |
430 | 由于笔者水平有限,若文中有错误还请指出,谢谢。
431 |
432 | 注:该源码分析对应SpringBoot版本为**2.1.0.RELEASE**,本文对应的SpringBoot源码解析项目github地址:https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE
433 |
434 |
--------------------------------------------------------------------------------
/SpringBoot/9 SpringBoot事件监听机制源码分析(上) SpringBoot源码(九).md:
--------------------------------------------------------------------------------
1 | **SpringBoot中文注释项目Github地址:**
2 |
3 | https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE
4 |
5 |
6 |
7 | 本篇接 [SpringApplication对象是如何构建的? SpringBoot源码(八)](https://juejin.im/post/5e82bac9518825737a314096)
8 |
9 | # 1 温故而知新
10 | 温故而知新,我们来简单回顾一下上篇的内容,上一篇我们分析了**SpringApplication对象的构建过程及SpringBoot自己实现的一套SPI机制**,现将关键步骤再浓缩总结下:
11 | 1. `SpringApplication`对象的构造过程其实就是给`SpringApplication`类的**6**个成员变量赋值;
12 | 2. SpringBoot通过以下步骤实现自己的SPI机制:
13 | * 1)首先获取线程上下文类加载器;
14 | * 2)然后利用上下文类加载器从`spring.factories`配置文件中**加载所有的SPI扩展实现类并放入缓存中**;
15 | * 3)根据SPI接口从缓存中取出相应的SPI扩展实现类;
16 | * 4)实例化从缓存中取出的SPI扩展实现类并返回。
17 |
18 | # 2 引言
19 | 在SpringBoot启动过程中,每个不同的启动阶段会分别广播不同的内置生命周期事件,然后相应的监听器会监听这些事件来执行一些初始化逻辑工作比如`ConfigFileApplicationListener`会监听`onApplicationEnvironmentPreparedEvent`事件来加载配置文件`application.properties`的环境变量等。
20 |
21 | 因此本篇内容将来分析下SpringBoot的事件监听机制的源码。
22 |
23 | # 3 SpringBoot广播内置生命周期事件流程分析
24 | 为了探究SpringBoot广播内置生命周期事件流程,我们再来回顾一下SpringBoot的启动流程代码:
25 | ```java
26 | // SpringApplication.java
27 |
28 | public ConfigurableApplicationContext run(String... args) {
29 | StopWatch stopWatch = new StopWatch();
30 | stopWatch.start();
31 | ConfigurableApplicationContext context = null;
32 | Collection exceptionReporters = new ArrayList<>();
33 | configureHeadlessProperty();
34 | // 【0】新建一个SpringApplicationRunListeners对象用于发射SpringBoot启动过程中的生命周期事件
35 | SpringApplicationRunListeners listeners = getRunListeners(args);
36 | // 【1】》》》》》发射【ApplicationStartingEvent】事件,标志SpringApplication开始启动
37 | listeners.starting();
38 | try {
39 | ApplicationArguments applicationArguments = new DefaultApplicationArguments(
40 | args);
41 | // 【2】》》》》》发射【ApplicationEnvironmentPreparedEvent】事件,此时会去加载application.properties等配置文件的环境变量,同时也有标志环境变量已经准备好的意思
42 | ConfigurableEnvironment environment = prepareEnvironment(listeners,
43 | applicationArguments);
44 | configureIgnoreBeanInfo(environment);
45 | Banner printedBanner = printBanner(environment);
46 | context = createApplicationContext();
47 | exceptionReporters = getSpringFactoriesInstances(
48 | SpringBootExceptionReporter.class,
49 | new Class[] { ConfigurableApplicationContext.class }, context);
50 | // 【3】》》》》》发射【ApplicationContextInitializedEvent】事件,标志context容器被创建且已准备好
51 | // 【4】》》》》》发射【ApplicationPreparedEvent】事件,标志Context容器已经准备完成
52 | prepareContext(context, environment, listeners, applicationArguments,
53 | printedBanner);
54 | refreshContext(context);
55 | afterRefresh(context, applicationArguments);
56 | stopWatch.stop();
57 | if (this.logStartupInfo) {
58 | new StartupInfoLogger(this.mainApplicationClass)
59 | .logStarted(getApplicationLog(), stopWatch);
60 | }
61 | // 【5】》》》》》发射【ApplicationStartedEvent】事件,标志spring容器已经刷新,此时所有的bean实例都已经加载完毕
62 | listeners.started(context);
63 | callRunners(context, applicationArguments);
64 | }
65 | // 【6】》》》》》发射【ApplicationFailedEvent】事件,标志SpringBoot启动失败
66 | catch (Throwable ex) {
67 | handleRunFailure(context, ex, exceptionReporters, listeners);
68 | throw new IllegalStateException(ex);
69 | }
70 | try {
71 | // 【7】》》》》》发射【ApplicationReadyEvent】事件,标志SpringApplication已经正在运行即已经成功启动,可以接收服务请求了。
72 | listeners.running(context);
73 | }
74 | catch (Throwable ex) {
75 | handleRunFailure(context, ex, exceptionReporters, null);
76 | throw new IllegalStateException(ex);
77 | }
78 | return context;
79 | }
80 | ```
81 | 可以看到SpringBoot在启动过程中首先会先新建一个`SpringApplicationRunListeners`对象用于发射SpringBoot启动过程中的各种生命周期事件,比如发射`ApplicationStartingEvent`,`ApplicationEnvironmentPreparedEvent`和`ApplicationContextInitializedEvent`等事件,然后相应的监听器会执行一些SpringBoot启动过程中的初始化逻辑。那么,监听这些SpringBoot的生命周期事件的监听器们是何时被加载实例化的呢?还记得上篇文章在分析`SpringApplication`的构建过程吗?没错,这些执行初始化逻辑的监听器们正是在`SpringApplication`的构建过程中根据`ApplicationListener`接口去`spring.factories`配置文件中加载并实例化的。
82 | # 3.1 为广播SpringBoot内置生命周期事件做前期准备
83 | # 3.1.1 加载ApplicationListener监听器实现类
84 | 我们再来回顾下[SpringApplication对象是如何构建的? SpringBoot源码(八)](https://juejin.im/post/5e82bac9518825737a314096)一文中讲到在构建`SpringApplication`对象时的`setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));`这句代码。
85 |
86 | 这句代码做的事情就是从`spring.factories`中加载出`ApplicationListener`事件监听接口的SPI扩展实现类然后添加到`SpringApplication`对象的`listeners`集合中,用于后续监听SpringBoot启动过程中的事件,来执行一些初始化逻辑工作。
87 |
88 | SpringBoot启动时的具体监听器们都实现了`ApplicationListener`接口,其在`spring.factories`部分配置如下:
89 |
90 | 
91 |
92 | 不过在调试时,会从所有的spring.factories配置文件中加载监听器,最终加载了10个监听器。如下图:
93 |
94 |
95 | 
96 |
97 | # 3.1.2 加载SPI扩展类EventPublishingRunListener
98 | 前面讲到,在SpringBoot的启动过程中首先会先新建一个`SpringApplicationRunListeners`对象用于发射SpringBoot启动过程中的生命周期事件,即我们现在来看下`SpringApplicationRunListeners listeners = getRunListeners(args);`这句代码:
99 |
100 | ```java
101 | // SpringApplication.java
102 |
103 | private SpringApplicationRunListeners getRunListeners(String[] args) {
104 | // 构造一个由SpringApplication.class和String[].class组成的types
105 | Class>[] types = new Class>[] { SpringApplication.class, String[].class };
106 | // 1) 根据SpringApplicationRunListener接口去spring.factories配置文件中加载其SPI扩展实现类
107 | // 2) 构建一个SpringApplicationRunListeners对象并返回
108 | return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
109 | SpringApplicationRunListener.class, types, this, args));
110 | }
111 |
112 | ```
113 |
114 | 我们将重点放到`getSpringFactoriesInstances(
115 | SpringApplicationRunListener.class, types, this, args)`这句代码,`getSpringFactoriesInstances`这个方法我们已经很熟悉,在上一篇分析SpringBoot的SPI机制时已经详细分析过这个方法。可以看到SpringBoot此时又是根据`SpringApplicationRunListener`这个SPI接口去`spring.factories`中加载相应的SPI扩展实现类,我们直接去`spring.factories`中看看`SpringApplicationRunListener`有哪些SPI实现类:
116 |
117 | 
118 | 由上图可以看到,`SpringApplicationRunListener`只有`EventPublishingRunListener`这个SPI实现类
119 | `EventPublishingRunListener`这个哥们在SpringBoot的启动过程中尤其重要,由其在SpringBoot启动过程的不同阶段发射不同的SpringBoot的生命周期事件,**即`SpringApplicationRunListeners`对象没有承担广播事件的职责,而最终是委托`EventPublishingRunListener`这个哥们来广播事件的。**
120 |
121 | 因为从`spring.factories`中加载`EventPublishingRunListener`类后还会实例化该类,那么我们再跟进`EventPublishingRunListener`的源码,看看其是如何承担发射SpringBoot生命周期事件这一职责的?
122 | ```java
123 | // EventPublishingRunListener.java
124 |
125 | public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
126 |
127 | private final SpringApplication application;
128 |
129 | private final String[] args;
130 | /**
131 | * 拥有一个SimpleApplicationEventMulticaster事件广播器来广播事件
132 | */
133 | private final SimpleApplicationEventMulticaster initialMulticaster;
134 |
135 | public EventPublishingRunListener(SpringApplication application, String[] args) {
136 | this.application = application;
137 | this.args = args;
138 | // 新建一个事件广播器SimpleApplicationEventMulticaster对象
139 | this.initialMulticaster = new SimpleApplicationEventMulticaster();
140 | // 遍历在构造SpringApplication对象时从spring.factories配置文件中获取的事件监听器
141 | for (ApplicationListener> listener : application.getListeners()) {
142 | // 将从spring.factories配置文件中获取的事件监听器们添加到事件广播器initialMulticaster对象的相关集合中
143 | this.initialMulticaster.addApplicationListener(listener);
144 | }
145 | }
146 |
147 | @Override
148 | public int getOrder() {
149 | return 0;
150 | }
151 | // 》》》》》发射【ApplicationStartingEvent】事件
152 | @Override
153 | public void starting() {
154 | this.initialMulticaster.multicastEvent(
155 | new ApplicationStartingEvent(this.application, this.args));
156 | }
157 | // 》》》》》发射【ApplicationEnvironmentPreparedEvent】事件
158 | @Override
159 | public void environmentPrepared(ConfigurableEnvironment environment) {
160 | this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
161 | this.application, this.args, environment));
162 | }
163 | // 》》》》》发射【ApplicationContextInitializedEvent】事件
164 | @Override
165 | public void contextPrepared(ConfigurableApplicationContext context) {
166 | this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(
167 | this.application, this.args, context));
168 | }
169 | // 》》》》》发射【ApplicationPreparedEvent】事件
170 | @Override
171 | public void contextLoaded(ConfigurableApplicationContext context) {
172 | for (ApplicationListener> listener : this.application.getListeners()) {
173 | if (listener instanceof ApplicationContextAware) {
174 | ((ApplicationContextAware) listener).setApplicationContext(context);
175 | }
176 | context.addApplicationListener(listener);
177 | }
178 | this.initialMulticaster.multicastEvent(
179 | new ApplicationPreparedEvent(this.application, this.args, context));
180 | }
181 | // 》》》》》发射【ApplicationStartedEvent】事件
182 | @Override
183 | public void started(ConfigurableApplicationContext context) {
184 | context.publishEvent(
185 | new ApplicationStartedEvent(this.application, this.args, context));
186 | }
187 | // 》》》》》发射【ApplicationReadyEvent】事件
188 | @Override
189 | public void running(ConfigurableApplicationContext context) {
190 | context.publishEvent(
191 | new ApplicationReadyEvent(this.application, this.args, context));
192 | }
193 | // 》》》》》发射【ApplicationFailedEvent】事件
194 | @Override
195 | public void failed(ConfigurableApplicationContext context, Throwable exception) {
196 | ApplicationFailedEvent event = new ApplicationFailedEvent(this.application,
197 | this.args, context, exception);
198 | if (context != null && context.isActive()) {
199 | // Listeners have been registered to the application context so we should
200 | // use it at this point if we can
201 | context.publishEvent(event);
202 | }
203 | else {
204 | // An inactive context may not have a multicaster so we use our multicaster to
205 | // call all of the context's listeners instead
206 | if (context instanceof AbstractApplicationContext) {
207 | for (ApplicationListener> listener : ((AbstractApplicationContext) context)
208 | .getApplicationListeners()) {
209 | this.initialMulticaster.addApplicationListener(listener);
210 | }
211 | }
212 | this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
213 | this.initialMulticaster.multicastEvent(event);
214 | }
215 | }
216 |
217 | // ...省略非关键代码
218 | }
219 |
220 | ```
221 |
222 | 可以看到`EventPublishingRunListener`类实现了`SpringApplicationRunListener`接口,`SpringApplicationRunListener`接口定义了SpringBoot启动时发射生命周期事件的接口方法,而`EventPublishingRunListener`类正是通过实现`SpringApplicationRunListener`接口的`starting`,`environmentPrepared`和`contextPrepared`等方法来广播SpringBoot不同的生命周期事件,我们直接看下`SpringApplicationRunListener`接口源码好了:
223 | ```java
224 | // SpringApplicationRunListener.java
225 |
226 | public interface SpringApplicationRunListener {
227 |
228 | void starting();
229 |
230 | void environmentPrepared(ConfigurableEnvironment environment);
231 |
232 | void contextPrepared(ConfigurableApplicationContext context);
233 |
234 | void contextLoaded(ConfigurableApplicationContext context);
235 |
236 | void started(ConfigurableApplicationContext context);
237 |
238 | void running(ConfigurableApplicationContext context);
239 |
240 | void failed(ConfigurableApplicationContext context, Throwable exception);
241 | }
242 | ```
243 | 我们再接着分析`EventPublishingRunListener`这个类,可以看到其有一个重要的成员属性`initialMulticaster`,该成员属性是`SimpleApplicationEventMulticaster`类对象,该类正是承担了广播SpringBoot启动时生命周期事件的职责,**即`EventPublishingRunListener`对象没有承担广播事件的职责,而最终是委托`SimpleApplicationEventMulticaster`这个哥们来广播事件的。** 从`EventPublishingRunListener`的源码中也可以看到在`starting`,`environmentPrepared`和`contextPrepared`等方法中也正是通过调用`SimpleApplicationEventMulticaster`类对象的`multicastEvent`方法来广播事件的。
244 |
245 | > **思考** SpringBoot启动过程中发射事件时事件广播者是层层委托职责的,起初由`SpringApplicationRunListeners`对象承担,然后`SpringApplicationRunListeners`对象将广播事件职责委托给`EventPublishingRunListener`对象,最终`EventPublishingRunListener`对象将广播事件的职责委托给`SimpleApplicationEventMulticaster`对象。**为什么要层层委托这么做呢?** 这个值得大家思考。
246 |
247 | 前面讲到从`spring.factories`中加载出`EventPublishingRunListener`类后会实例化,而实例化必然会通过`EventPublishingRunListener`的构造函数来进行实例化,因此我们接下来分析下`EventPublishingRunListener`的构造函数源码:
248 | ```java
249 | // EventPublishingRunListener.java
250 |
251 | public EventPublishingRunListener(SpringApplication application, String[] args) {
252 | this.application = application;
253 | this.args = args;
254 | // 新建一个事件广播器SimpleApplicationEventMulticaster对象
255 | this.initialMulticaster = new SimpleApplicationEventMulticaster();
256 | // 遍历在构造SpringApplication对象时从spring.factories配置文件中获取的事件监听器
257 | for (ApplicationListener> listener : application.getListeners()) {
258 | // 将从spring.factories配置文件中获取的事件监听器们添加到事件广播器initialMulticaster对象的相关集合中
259 | this.initialMulticaster.addApplicationListener(listener);
260 | }
261 | }
262 | ```
263 | 可以看到在`EventPublishingRunListener`的构造函数中有一个`for`循环会遍历之前从`spring.factories`中加载的监听器们,然后添加到集合中缓存起来,用于以后广播各种事件时直接从这个集合中取出来即可,而不用再去`spring.factories`中加载,提高效率。
264 |
265 | # 3.2 广播SpringBoot的内置生命周期事件
266 | 从`spring.factories`配置文件中加载并实例化`EventPublishingRunListener`对象后,那么在在SpringBoot的启动过程中会发射一系列SpringBoot内置的生命周期事件,我们再来回顾下SpringBoot启动过程中的源码:
267 | ```java
268 | // SpringApplication.java
269 |
270 | public ConfigurableApplicationContext run(String... args) {
271 | StopWatch stopWatch = new StopWatch();
272 | stopWatch.start();
273 | ConfigurableApplicationContext context = null;
274 | Collection exceptionReporters = new ArrayList<>();
275 | configureHeadlessProperty();
276 | // 【0】新建一个SpringApplicationRunListeners对象用于发射SpringBoot启动过程中的生命周期事件
277 | SpringApplicationRunListeners listeners = getRunListeners(args);
278 | // 【1】》》》》》发射【ApplicationStartingEvent】事件,标志SpringApplication开始启动
279 | listeners.starting();
280 | try {
281 | ApplicationArguments applicationArguments = new DefaultApplicationArguments(
282 | args);
283 | // 【2】》》》》》发射【ApplicationEnvironmentPreparedEvent】事件,此时会去加载application.properties等配置文件的环境变量,同时也有标志环境变量已经准备好的意思
284 | ConfigurableEnvironment environment = prepareEnvironment(listeners,
285 | applicationArguments);
286 | configureIgnoreBeanInfo(environment);
287 | Banner printedBanner = printBanner(environment);
288 | context = createApplicationContext();
289 | exceptionReporters = getSpringFactoriesInstances(
290 | SpringBootExceptionReporter.class,
291 | new Class[] { ConfigurableApplicationContext.class }, context);
292 | // 【3】》》》》》发射【ApplicationContextInitializedEvent】事件,标志context容器被创建且已准备好
293 | // 【4】》》》》》发射【ApplicationPreparedEvent】事件,标志Context容器已经准备完成
294 | prepareContext(context, environment, listeners, applicationArguments,
295 | printedBanner);
296 | refreshContext(context);
297 | afterRefresh(context, applicationArguments);
298 | stopWatch.stop();
299 | if (this.logStartupInfo) {
300 | new StartupInfoLogger(this.mainApplicationClass)
301 | .logStarted(getApplicationLog(), stopWatch);
302 | }
303 | // 【5】》》》》》发射【ApplicationStartedEvent】事件,标志spring容器已经刷新,此时所有的bean实例都已经加载完毕
304 | listeners.started(context);
305 | callRunners(context, applicationArguments);
306 | }
307 | // 【6】》》》》》发射【ApplicationFailedEvent】事件,标志SpringBoot启动失败
308 | catch (Throwable ex) {
309 | handleRunFailure(context, ex, exceptionReporters, listeners);
310 | throw new IllegalStateException(ex);
311 | }
312 | try {
313 | // 【7】》》》》》发射【ApplicationReadyEvent】事件,标志SpringApplication已经正在运行即已经成功启动,可以接收服务请求了。
314 | listeners.running(context);
315 | }
316 | catch (Throwable ex) {
317 | handleRunFailure(context, ex, exceptionReporters, null);
318 | throw new IllegalStateException(ex);
319 | }
320 | return context;
321 | }
322 | ```
323 | 可以看到在SpringBoot的启动过程中总共会发射7种不同类型的生命周期事件,来标志SpringBoot的不同启动阶段,同时,这些生命周期事件的监听器们也会执行一些启动过程中的初始化逻辑,关于这些监听器的初始化逻辑将在下一篇内容中会分析。以下是SpringBoot启动过程中要发射的事件类型,其中`ApplicationFailedEvent`在SpringBoot启动过程中遇到异常才会发射:
324 | 1. `ApplicationStartingEvent`
325 | 2. `ApplicationEnvironmentPreparedEvent`
326 | 3. `ApplicationContextInitializedEvent`
327 | 4. `ApplicationPreparedEvent`
328 | 5. `ApplicationStartedEvent`
329 | 6. `ApplicationFailedEvent`
330 | 7. `ApplicationReadyEvent`
331 |
332 | 我们以`listeners.starting();`这句代码为例,看看`EventPublishingRunListener`对象发射事件的源码:
333 | ```java
334 | // SpringApplicationRunListeners.java
335 |
336 | public void starting() {
337 | // 遍历listeners集合,这里实质取出的就是刚才从spring.factories中取出的SPI实现类EventPublishingRunListener
338 | // 而EventPublishingRunListener对象承担了SpringBoot启动过程中负责广播不同的生命周期事件
339 | for (SpringApplicationRunListener listener : this.listeners) {
340 | // 调用EventPublishingRunListener的starting方法来广播ApplicationStartingEvent事件
341 | listener.starting();
342 | }
343 | }
344 | ```
345 | 继续跟进`listener.starting();`的源码:
346 | ```java
347 | EventPublishingRunListener.java
348 |
349 | // 》》》》》发射【ApplicationStartingEvent】事件
350 | public void starting() {
351 | // EventPublishingRunListener对象将发布ApplicationStartingEvent这件事情委托给了initialMulticaster对象
352 | // 调用initialMulticaster的multicastEvent方法来发射ApplicationStartingEvent事件
353 | this.initialMulticaster.multicastEvent(
354 | new ApplicationStartingEvent(this.application, this.args));
355 | }
356 | ```
357 | 可以看到,`EventPublishingRunListener`对象将发布`ApplicationStartingEvent`这件事情委托给了`SimpleApplicationEventMulticaster`对象`initialMulticaster`,
358 | ,而`initialMulticaster`对象最终会调用其`multicastEvent`方法来发射`ApplicationStartingEvent`事件。关于`SimpleApplicationEventMulticaster`类如何广播事件,笔者已经在[Spring是如何实现事件监听机制的? Spring源码(二)](https://juejin.im/post/5e421bfc6fb9a07cd80f1354)这篇文章已经详细分析,这里不再赘述。
359 |
360 | 关于SpringBoot启动过程中发射其他生命周期事件的源码这里不再分析
361 |
362 | # 4 SpringBoot的内置生命周期事件总结
363 | 好了,前面已经分析了SpringBoot启动过程中要发射的各种生命周期事件,下面列一个表格总结下:
364 |
365 | 
366 |
367 |
368 | # 5 小结
369 | SpringBoot启动过程中广播生命周期事件的源码分析就到此结束了,下一篇会继续介绍监听这些生命周期事件的监听器们。我们再回顾本篇内容总结下关键点:
370 |
371 | SpringBoot启动过程中会发射7种类型的生命周期事件,标志不同的启动阶段,然后相应的监听器会监听这些事件来执行一些初始化逻辑工作。
372 |
373 | **【源码笔记】Github源码分析项目上线啦!!!下面是笔记的Github地址:**
374 |
375 | https://github.com/yuanmabiji/Java-SourceCode-Blogs
376 |
377 | **原创不易,帮忙Star一下呗**!
378 |
379 |
--------------------------------------------------------------------------------
/Spring/2 Spring是如何实现事件监听机制的? Spring源码(二).md:
--------------------------------------------------------------------------------
1 | **注意:该源码分析对应版本为spring5.1.x**
2 |
3 | # 1,概述
4 |
5 | 本篇开始分析Spring的事件机制源码,因为Spring的事件机制实质是观察者(发布订阅)模式的实现,因此要想搞清楚Spring的事件机制,因此得知道观察者模式是什么。
6 |
7 | 同时,[本文接模仿Spring事件机制实现自定义事件驱动编程--Spring的事件机制源码分析(一)](https://github.com/yuanmabiji/Java-SourceCode-Blogs/blob/master/Spring/1%20%E6%A8%A1%E4%BB%BFSpring%E4%BA%8B%E4%BB%B6%E6%9C%BA%E5%88%B6%E5%AE%9E%E7%8E%B0%E8%87%AA%E5%AE%9A%E4%B9%89%E4%BA%8B%E4%BB%B6%E9%A9%B1%E5%8A%A8%E7%BC%96%E7%A8%8B%20Spring%E6%BA%90%E7%A0%81%EF%BC%88%E4%B8%80%EF%BC%89.md)一文,前面自己实现了一个简单的事件驱动编程的简单demo后,那么此时分析Spring的事件机制源码就简单多了。
8 |
9 | 在开始正题前,先聊聊研究源码的感受:研究源码前那么必须先搞清楚类与类之间的关系,比如某个接口有哪些实现类,某个父类有哪些子类,子类与子类之间的关系,这些类之间的关系捋清楚了,那么再下手研究源码就容易很多。总之不能一下子就进入源码的某个细节,这样子就会造成只见冰山一角而看不到全貌的感觉。
10 |
11 | 好了,下面开始进入正题,开始学习Spring的事件机制。因为编码一般都是面向接口编程,那么我们先从事件机制的相关接口或抽象类开始分析。
12 |
13 | Spring事件机制涉及的重要的类主要有以下四个:
14 |
15 | - ApplicationEvent:事件,该抽象类是所有Spring事件的父类,可携带数据比如事件发生时间timestamp。
16 | - ApplicationListener:事件监听器,该接口被所有的事件监听器实现,基于标准的java的EventListener接口实现观察者模式。
17 | - ApplicationEventMulticaster:事件管理者,管理监听器和发布事件,ApplicationContext通过委托ApplicationEventMulticaster来 发布事件
18 | - ApplicationEventPublisher:事件发布者,该接口封装了事件有关的公共方法,作为ApplicationContext的超级街廓,也是委托 ApplicationEventMulticaster完成事件发布。
19 |
20 | # 2,Spring事件涉及类源码分析
21 |
22 | 事件相关的主要接口类上面已经介绍完毕,下面来看下每个接口及其子类之间的关系。
23 |
24 | ## 2.1 ApplicationEvent
25 |
26 | 首先看下类图如下:
27 |
28 | 
29 |
30 | **图1**
31 |
32 | 其接口代码如下:
33 |
34 | ```
35 | // 事件抽象类,这个是所有Spring事件的父类
36 | public abstract class ApplicationEvent extends EventObject {
37 |
38 | /** use serialVersionUID from Spring 1.2 for interoperability. */
39 | private static final long serialVersionUID = 7099057708183571937L;
40 |
41 | /** System time when the event happened. */
42 | private final long timestamp;
43 |
44 |
45 | /**
46 | * Create a new ApplicationEvent.
47 | * @param source the object on which the event initially occurred (never {@code null})
48 | */
49 | public ApplicationEvent(Object source) {
50 | super(source);
51 | this.timestamp = System.currentTimeMillis();
52 | }
53 |
54 |
55 | /**
56 | * Return the system time in milliseconds when the event happened.
57 | */
58 | public final long getTimestamp() {
59 | return this.timestamp;
60 | }
61 |
62 | }
63 | ```
64 |
65 | 
66 |
67 | ApplicationEvent类定义了一些属性比如timestamp,这个表示事件的发生时间,因此可以通过事件来传递一些参数。
68 |
69 | 图1是ApplicationEvent部分重要的子类关系图,其中ApplicationEvent最重要的子类是ApplicationContextEvent抽象类,ApplicationContextEvent是spring容器Context生命周期事件的基类,ApplicationContextEvent的有四个子类,如下:
70 |
71 | - ContextRefreshedEvent:当spring容器context刷新时触发
72 | - ContextStartedEvent:当spring容器context启动后触发
73 | - ContextStoppedEvent:当spring容器context停止时触发
74 | - ContextClosedEvent:当spring容器context关闭时触发,容器被关闭时,其管理的所有单例Bean都被销毁。
75 |
76 | 以上四个事件就是spring容器生命周期的四个事件,当每个事件触发时,相关的监听器就会监听到相应事件,然后触发onApplicationEvent方法,此时就可以做一些容器,同时这些容器事件跟spring的后置处理器一样,留给用户扩展自定义逻辑,作为暴露的扩展点。
77 |
78 | 以ContextRefreshedEvent事件为例讲解下相关监听类,通过idea全局搜索"(ContextRefreshedEvent"关键字,得到以下截图:
79 |
80 | 
81 |
82 | 从上图可以看到spring-webmvc模块的FrameworkServlet,spring-context模块的ScheduledAnnotationBeanPostProcessor,和spring-jms模块的JmsListenerEndpointRegistry等类订阅了ContextRefreshedEvent事件,那么在容器刷新的时候这几个类将会监听到ContextRefreshedEvent事件,执行一些初始化逻辑。这一块后面有时间再研究,TODO。
83 |
84 | 下面粘贴下ApplicationContextEvent的四个子类的实现代码,基本都是继承ApplicationContextEvent父类,没有什么逻辑,更多是一个生命周期事件的标志类。
85 |
86 | ```
87 | public class ContextRefreshedEvent extends ApplicationContextEvent {
88 |
89 | // 当springcontext已经被初始化或者刷新的时候,创建该事件
90 | public ContextRefreshedEvent(ApplicationContext source) {
91 | super(source);
92 | }
93 |
94 | }
95 |
96 | public class ContextStartedEvent extends ApplicationContextEvent {
97 |
98 | // 当springContext已经启动的时候,创建该事件
99 | public ContextStartedEvent(ApplicationContext source) {
100 | super(source);
101 | }
102 |
103 | }
104 |
105 | public class ContextStoppedEvent extends ApplicationContextEvent {
106 |
107 | // 当springContext已经停止时创建该事件
108 | public ContextStoppedEvent(ApplicationContext source) {
109 | super(source);
110 | }
111 |
112 | }
113 |
114 | public class ContextClosedEvent extends ApplicationContextEvent {
115 |
116 | // 当springContext关闭时创建该事件
117 | public ContextClosedEvent(ApplicationContext source) {
118 | super(source);
119 | }
120 |
121 | }
122 | ```
123 |
124 | 
125 |
126 | ## 2.2 ApplicationListener
127 |
128 | ```
129 | @FunctionalInterface
130 | public interface ApplicationListener extends EventListener {
131 |
132 | /**
133 | * Handle an application event.
134 | * @param event the event to respond to
135 | */
136 | void onApplicationEvent(E event);
137 |
138 | }
139 | ```
140 |
141 | 
142 |
143 | ApplicationListener是所有事件监听器的父接口,事件监听器监听某个事件必须要实现该接口。这里值得注意的是ApplicationListener接口的参数化类型,这样的话具体的监听器实现该接口时可以指定特定的事件类,当传入的事件向下转型时不是该特定的事件时,此时会抛出类转换异常。不过一般使用的时候会先判断下该事件类型是否属于某种事件,然后再执行相关逻辑,如下代码:
144 |
145 | ```
146 | @Override
147 | public void onApplicationEvent(ApplicationEvent event) {
148 | if (event instanceof ApplicationStartingEvent) {
149 | onApplicationStartingEvent((ApplicationStartingEvent) event);
150 | }
151 | else if (event instanceof ApplicationEnvironmentPreparedEvent) {
152 | onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
153 | }
154 | else if (event instanceof ApplicationPreparedEvent) {
155 | onApplicationPreparedEvent((ApplicationPreparedEvent) event);
156 | }
157 | else if (event instanceof ContextClosedEvent
158 | && ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
159 | onContextClosedEvent();
160 | }
161 | else if (event instanceof ApplicationFailedEvent) {
162 | onApplicationFailedEvent();
163 | }
164 | }
165 | ```
166 |
167 | 
168 |
169 | 由于ApplicationListener接口的具体实现类太多,因此就不贴类关系图了。
170 |
171 | ## 2.3 ApplicationEventMulticaster
172 |
173 | 首先看下类图,
174 |
175 | 
176 |
177 | ApplicationEventMulticaster接口功能主要用来广播事件给所有listener,主要定义了增删改监听器和广播事件的接口方法,代码如下:
178 |
179 | ```
180 | public interface ApplicationEventMulticaster {
181 | void addApplicationListener(ApplicationListener> var1);
182 |
183 | void addApplicationListenerBean(String var1);
184 |
185 | void removeApplicationListener(ApplicationListener> var1);
186 |
187 | void removeApplicationListenerBean(String var1);
188 |
189 | void removeAllListeners();
190 |
191 | void multicastEvent(ApplicationEvent var1);
192 |
193 | void multicastEvent(ApplicationEvent var1, @Nullable ResolvableType var2);
194 | }
195 | ```
196 |
197 | 
198 |
199 | AbstractApplicationEventMulticaster是ApplicationEventMulticaster接口的抽象实现,提供最基本的监听器注册的方法。注册监听器时一般不允许相同监听器注册多个实例,因此使用Set集合,用于去重。然后实现广播事件的具体实现没有在这里实现,而是交给子类SimpleApplicationEventMulticaster去实现。
200 |
201 | AbstractApplicationEventMulticaster抽象类的关键代码如下:
202 |
203 | ```
204 | // AbstractApplicationEventMulticaster.java
205 |
206 | /**
207 | * 获取事件监听器的帮助类,拥有Set>属性
208 | */
209 | private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);
210 | /**
211 | * ListenerRetriever缓存
212 | * key:ListenerCacheKey value:ListenerRetriever
213 | */
214 | final Map retrieverCache = new ConcurrentHashMap<>(64);
215 |
216 | // 添加spring监听器到ListenerRetriever的applicationListeners集合中
217 | public void addApplicationListener(ApplicationListener> listener) {
218 | synchronized (this.retrievalMutex) {
219 | // Explicitly remove target for a proxy, if registered already,
220 | // in order to avoid double invocations of the same listener.
221 | Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
222 | if (singletonTarget instanceof ApplicationListener) {
223 | this.defaultRetriever.applicationListeners.remove(singletonTarget);
224 | }
225 | this.defaultRetriever.applicationListeners.add(listener);
226 | this.retrieverCache.clear();
227 | }
228 | }
229 |
230 | // 移除监听器
231 | public void removeApplicationListener(ApplicationListener> listener) {
232 | synchronized (this.retrievalMutex) {
233 | this.defaultRetriever.applicationListeners.remove(listener);
234 | this.retrieverCache.clear();
235 | }
236 | }
237 |
238 | // 移除所有监听器
239 | public void removeAllListeners() {
240 | synchronized (this.retrievalMutex) {
241 | this.defaultRetriever.applicationListeners.clear();
242 | this.defaultRetriever.applicationListenerBeans.clear();
243 | this.retrieverCache.clear();
244 | }
245 | }
246 |
247 | // 利用defaultRetriever得到所有的监听器
248 | protected Collection> getApplicationListeners() {
249 | synchronized (this.retrievalMutex) {
250 | return this.defaultRetriever.getApplicationListeners();
251 | }
252 | }
253 |
254 |
255 |
256 | ```
257 |
258 | 
259 |
260 | 根据上面代码,大家注意到了AbstractApplicationEventMulticaster的增加,删除和后去listeners是委托给其内部类ListenerRetriever去获取的,因为ListenerRetriever内部维护了监听器的集合Set>。下面看看ListenerRetriever这个内部类关键代码:
261 |
262 | ```
263 | private class ListenerRetriever {
264 | /**
265 | * 监听器集合
266 | */
267 | public final Set> applicationListeners = new LinkedHashSet<>();
268 |
269 | public final Set applicationListenerBeans = new LinkedHashSet<>();
270 |
271 | private final boolean preFiltered;
272 |
273 | public ListenerRetriever(boolean preFiltered) {
274 | this.preFiltered = preFiltered;
275 | }
276 |
277 | /**
278 | * 获取所有的spring监听器
279 | * @return
280 | */
281 | public Collection> getApplicationListeners() {
282 | List> allListeners = new ArrayList<>(
283 | this.applicationListeners.size() + this.applicationListenerBeans.size());
284 | allListeners.addAll(this.applicationListeners);
285 | if (!this.applicationListenerBeans.isEmpty()) {
286 | BeanFactory beanFactory = getBeanFactory();
287 | for (String listenerBeanName : this.applicationListenerBeans) {
288 | try {
289 | ApplicationListener> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
290 | if (this.preFiltered || !allListeners.contains(listener)) {
291 | allListeners.add(listener);
292 | }
293 | }
294 | catch (NoSuchBeanDefinitionException ex) {
295 | // Singleton listener instance (without backing bean definition) disappeared -
296 | // probably in the middle of the destruction phase
297 | }
298 | }
299 | }
300 | if (!this.preFiltered || !this.applicationListenerBeans.isEmpty()) {
301 | AnnotationAwareOrderComparator.sort(allListeners);
302 | }
303 | return allListeners;
304 | }
305 | }
306 | ```
307 |
308 | 
309 |
310 | 下面再来看下承担广播事件的SimpleApplicationEventMulticaster类的关键代码:
311 |
312 | ```
313 | public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
314 | // 执行广播异步事件的线程
315 | @Nullable
316 | private Executor taskExecutor;
317 | // 广播异步事件的线程时出现异常时的处理器
318 | @Nullable
319 | private ErrorHandler errorHandler;
320 |
321 |
322 | /**
323 | * Create a new SimpleApplicationEventMulticaster.
324 | */
325 | public SimpleApplicationEventMulticaster() {
326 | }
327 |
328 |
329 | @Override
330 | public void multicastEvent(ApplicationEvent event) {
331 | multicastEvent(event, resolveDefaultEventType(event));
332 | }
333 |
334 | @Override
335 | public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
336 | ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
337 | // 获取执行异步任务的线程池,这里异步要外部指定一个线程池,注入进来
338 | Executor executor = getTaskExecutor();
339 | // 遍历每一个spring事件监听器
340 | for (ApplicationListener> listener : getApplicationListeners(event, type)) {
341 | // 若外部指定的线程池不为null,则异步广播事件
342 | if (executor != null) {
343 | executor.execute(() -> invokeListener(listener, event));
344 | }
345 | // executor为空,则单线程同步广播事件
346 | else {
347 | invokeListener(listener, event);
348 | }
349 | }
350 | }
351 |
352 | protected void invokeListener(ApplicationListener> listener, ApplicationEvent event) {
353 | ErrorHandler errorHandler = getErrorHandler();
354 | // errorHandler不为空的情况下,则会进入try...catch..代码块,这里会对异步广播事件发生的异常进行处理
355 | if (errorHandler != null) {
356 | try {
357 | // 这里真正执行广播事件的逻辑
358 | doInvokeListener(listener, event);
359 | }
360 | catch (Throwable err) {
361 | // 处理异常
362 | errorHandler.handleError(err);
363 | }
364 | }
365 | // errorHandler为空的情况下,则不对出现的异常进行处理
366 | else {
367 | doInvokeListener(listener, event);
368 | }
369 | }
370 |
371 | @SuppressWarnings({"rawtypes", "unchecked"})
372 | private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
373 | try {
374 | // 回调监听器onApplicationEvent方法,执行监听逻辑
375 | listener.onApplicationEvent(event);
376 | }
377 | catch (ClassCastException ex) {
378 | // 若出现异常,这里打印一些日志或将异常继续跑出去
379 | String msg = ex.getMessage();
380 | if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
381 | // Possibly a lambda-defined listener which we could not resolve the generic event type for
382 | // -> let's suppress the exception and just log a debug message.
383 | Log logger = LogFactory.getLog(getClass());
384 | if (logger.isTraceEnabled()) {
385 | logger.trace("Non-matching event type for listener: " + listener, ex);
386 | }
387 | }
388 | else {
389 | throw ex;
390 | }
391 | }
392 | }
393 |
394 | }
395 | ```
396 |
397 | 
398 |
399 | SimpleApplicationEventMulticaster是ApplicationEventMulticaster的实现类,承担广播所有事件给注册的spring监听器, 让监听器自己去决定哪些事件是自己感兴趣的,监听器们将会执行instanof来判断是否是自己感兴趣的事件。默认情况下,所有监听器将会在调用线程中即单线程中同步阻塞执行,因此,若监听器数量过多或某个监听器执行时间过长 这将会导致spring容器启动时间过长。不过SimpleApplicationEventMulticaster也提供了异步广播时间的功能,通过taskExecutor来获取线程池,然后多线程广播事件,此外其还维护了一个errorHandler对象属性,异常处理器,errorHandler主要用来当异步广播事件时,若监听器执行异常时,此时利用其来处理catch住的异常。
400 |
401 | ## 2.4 ApplicationEventPublisher
402 |
403 | 同样,先来看下下面的类关系图
404 |
405 | 
406 |
407 | 可以看出所有Spring容器的父类接口ApplicationContext继承了ApplicationEventPublisher这个接口,因此spring容器一般是具有广播事件的功能。
408 |
409 | 下面来看下ApplicationEventPublisher的接口类代码:
410 |
411 | ```
412 | @FunctionalInterface
413 | public interface ApplicationEventPublisher {
414 | default void publishEvent(ApplicationEvent event) {
415 | this.publishEvent((Object)event);
416 | }
417 |
418 | void publishEvent(Object event);
419 | }
420 | ```
421 |
422 | 
423 |
424 | 该接口封装了发布事件的公共方法,作为ApplicationContext的超级接口,同事也是委托ApplicationEventMulticaster完成事件发布。
425 |
426 | 下面再来看下Spring容器实现了ApplicationEventPublisher接口后是如何来发布事件的,此时得先来看下spring容器的父类接口ApplicationContext,因为该接口继承了ApplicationEventPublisher接口,因此让spring容器具有了发布事件的功能。
427 |
428 | ```
429 | public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
430 | MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
431 |
432 | // 省略接口方法
433 |
434 | }
435 | ```
436 |
437 | 
438 |
439 | 那么spring容器是如何来发布事件的呢?前面已经讲过ApplicationEventMulticaster接口,没错,spring容器context正是委托其来实现发布事件的功能。因为AbstractApplicationContext实现了ConfigurableApplicationContext接口,通过该接口最终实现了ApplicationEventPublisher接口,spring容器发布事件的方法封装在AbstractApplicationContext的publishEvent方法中,
440 |
441 | 下面直接看下相关代码:
442 |
443 | ```
444 | public abstract class AbstractApplicationContext extends DefaultResourceLoader
445 | implements ConfigurableApplicationContext {
446 | /**
447 | * 父类context
448 | */
449 | @Nullable
450 | private ApplicationContext parent;
451 |
452 | /**
453 | * 在multicaster setup前,发布事件
454 | */
455 | @Nullable
456 | private Set earlyApplicationEvents;
457 |
458 | // 发布事件给所有事件监听器,
459 | protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
460 | Assert.notNull(event, "Event must not be null");
461 |
462 | // Decorate event as an ApplicationEvent if necessary
463 | ApplicationEvent applicationEvent;
464 | if (event instanceof ApplicationEvent) {
465 | applicationEvent = (ApplicationEvent) event;
466 | }
467 | else {
468 | applicationEvent = new PayloadApplicationEvent<>(this, event);
469 | if (eventType == null) {
470 | eventType = ((PayloadApplicationEvent>) applicationEvent).getResolvableType();
471 | }
472 | }
473 |
474 | // Multicast right now if possible - or lazily once the multicaster is initialized
475 | if (this.earlyApplicationEvents != null) {
476 | this.earlyApplicationEvents.add(applicationEvent);
477 | }
478 | else {
479 | getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
480 | }
481 |
482 | // Publish event via parent context as well...
483 | if (this.parent != null) {
484 | if (this.parent instanceof AbstractApplicationContext) {
485 | ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
486 | }
487 | else {
488 | this.parent.publishEvent(event);
489 | }
490 | }
491 | }
492 | }
493 | ```
494 |
495 | 
496 |
497 | 最后,弄清楚该源码机制后,自己再动手实操一下,推荐阅读下面的实操文章:
498 |
499 | [spring 自定义事件发布及监听(简单实例)](https://www.cnblogs.com/xinde123/p/8918714.html)
500 |
501 |
502 |
503 | 小结:这篇文章是本人第二篇源码解析的文章,写作速度仍然很慢,希望以后思路捋清楚后能快点写完,加油。
504 |
505 |
506 |
507 | 参考:
508 |
509 | https://spring.io/docs
510 |
511 |
--------------------------------------------------------------------------------
/Disruptor/Disruptor广播模式与执行顺序链源码分析.md:
--------------------------------------------------------------------------------
1 | > 【源码笔记】专注于Java后端系列框架源码分析,Github地址:https://github.com/yuanmabiji/Java-SourceCode-Blogs
2 |
3 | # 1 前言
4 |
5 | 本篇文章开始`Disruptor`的源码分析,理解起来相对比较困难,特别是`Disruptor`的`sequenceBarrier`的理解,`sequenceBarrier`包括生产者与消费者之间的`gatingSequence`以及消费者与消费者之间的`dependentSequence`。此外,`Disruptor`源码中的`sequence`变量也比较多,需要捋清楚各种`sequence`的含义。最后,建议小伙伴们动手调试理解,效果会更好。
6 |
7 | # 2 Disruptor六边形DEMO
8 |
9 | 分析源码前,先来看看`Disruptor`六边形执行器链的`DEMO`。
10 |
11 | ```java
12 | public class LongEventMain
13 | {
14 | private static final int BUFFER_SIZE = 1024;
15 | public static void main(String[] args) throws Exception
16 | {
17 | // 1,构建disruptor
18 | final Disruptor disruptor = new Disruptor(
19 | new LongEventFactory(),
20 | BUFFER_SIZE,
21 | Executors.newFixedThreadPool(5), // 【注意点】线程池需要保证足够的线程:有多少个消费者就要有多少个线程,否则有些消费者将不会执行,生产者可能也会一直阻塞下去
22 | ProducerType.SINGLE,
23 | new YieldingWaitStrategy()
24 | );
25 |
26 | EventHandler eventHandler1 = new LongEventHandler1();
27 | EventHandler eventHandler2 = new LongEventHandler2();
28 | EventHandler eventHandler3 = new LongEventHandler3();
29 | EventHandler eventHandler4 = new LongEventHandler4();
30 | EventHandler eventHandler5 = new LongEventHandler5();
31 |
32 | // 方式1 构建串行执行顺序:
33 | /*disruptor
34 | .handleEventsWith(eventHandler1)
35 | .handleEventsWith(eventHandler2)
36 | .handleEventsWith(eventHandler3)
37 | .handleEventsWith(eventHandler4)
38 | .handleEventsWith(eventHandler5);*/
39 |
40 | // 方式2 构建并行执行顺序
41 | /*disruptor
42 | .handleEventsWith(eventHandler1, eventHandler2, eventHandler3, eventHandler4, eventHandler5);*/
43 |
44 | // 方式3 构建菱形执行顺序
45 | /*disruptor.handleEventsWith(eventHandler1, eventHandler2)
46 | .handleEventsWith(eventHandler3);*/
47 |
48 | // 2,构建eventHandler执行链
49 | // 方式4 构建六边形执行顺序
50 | disruptor.handleEventsWith(eventHandler1, eventHandler3);
51 | disruptor.after(eventHandler1).handleEventsWith(eventHandler2);
52 | disruptor.after(eventHandler3).handleEventsWith(eventHandler4);
53 | disruptor.after(eventHandler2, eventHandler4).handleEventsWith(eventHandler5);
54 |
55 | // 3, 启动disruptor即启动线程池线程执行BatchEventProcessor任务
56 | disruptor.start();
57 |
58 | // 4,生产者往ringBuffer生产数据并唤醒所有的消费者消费数据
59 | RingBuffer ringBuffer = disruptor.getRingBuffer();
60 | ByteBuffer bb = ByteBuffer.allocate(8);
61 | bb.putLong(0, 666);
62 | ringBuffer.publishEvent(new LongEventTranslatorOneArg(), bb);
63 | }
64 |
65 | static class LongEventTranslatorOneArg implements EventTranslatorOneArg {
66 | @Override
67 | public void translateTo(LongEvent event, long sequence, ByteBuffer buffer) {
68 | event.set(buffer.getLong(0));
69 | }
70 | }
71 |
72 | static class LongEvent
73 | {
74 | private long value;
75 |
76 | public void set(long value)
77 | {
78 | this.value = value;
79 | }
80 |
81 | public long get() {
82 | return this.value;
83 | }
84 | }
85 |
86 | static class LongEventFactory implements EventFactory
87 | {
88 | @Override
89 | public LongEvent newInstance()
90 | {
91 | return new LongEvent();
92 | }
93 | }
94 |
95 | static class LongEventHandler1 implements EventHandler
96 | {
97 | @Override
98 | public void onEvent(LongEvent event, long sequence, boolean endOfBatch)
99 | {
100 | System.out.println("LongEventHandler1-" + event.get() + " executed by " + Thread.currentThread().getName());
101 | }
102 | }
103 |
104 | static class LongEventHandler2 implements EventHandler
105 | {
106 | @Override
107 | public void onEvent(LongEvent event, long sequence, boolean endOfBatch)
108 | {
109 | System.out.println("LongEventHandler2-" + event.get() + " executed by " + Thread.currentThread().getName());
110 | }
111 | }
112 |
113 | static class LongEventHandler3 implements EventHandler
114 | {
115 | @Override
116 | public void onEvent(LongEvent event, long sequence, boolean endOfBatch)
117 | {
118 | System.out.println("LongEventHandler3-" + event.get() + " executed by " + Thread.currentThread().getName());
119 | }
120 | }
121 |
122 | static class LongEventHandler4 implements EventHandler
123 | {
124 | @Override
125 | public void onEvent(LongEvent event, long sequence, boolean endOfBatch)
126 | {
127 | System.out.println("LongEventHandler4-" + event.get() + " executed by " + Thread.currentThread().getName());
128 | }
129 | }
130 |
131 | static class LongEventHandler5 implements EventHandler
132 | {
133 | @Override
134 | public void onEvent(LongEvent event, long sequence, boolean endOfBatch)
135 | {
136 | System.out.println("LongEventHandler5-" + event.get() + " executed by " + Thread.currentThread().getName());
137 | }
138 | }
139 | }
140 | ```
141 | 这个`Demo`也是`Disruptor`广播模式与执行顺序链构建的`Demo`,有以下值得注意的点:
142 | 1. 生产者总是要把`RingBuffer`填充完一圈后才会考虑追赶消费者进度的问题;
143 | 2. 线程池需要保证足够的线程:有多少个消费者就要有多少个线程,否则有些消费者将不会执行(消费者线程起不来),生产者生产完一圈`RingBuffer`后即使有新的数据生产者也会一直阻塞下去;
144 | 3. 消费者执行链中,每个消费者都是独立的消费线程,决定当前消费者消不消费的只有其依赖的消费者有无消费完,消费者进行消费第二个数据时无须等整个执行链执行完才能消费。比如有执行链:A->B-C,生产者在`Ringbuffer`中生产了2个数据,那么消费顺序可能为A->B->C->A->B-C,也可能为A->B-A->B->C->C,也可能为A->A->B->B->C->C等。
145 | 4. 生产者填充完第一圈`Ringbuffer`后,当要追赶消费者消费速度时,此时生产者能否继续生产取决于执行链最后一个消费者的消费速度。比如有执行链:A->B-C,生产者的生产速度取决于消费者C的消费速度。
146 |
147 | # 3 初始化Disruptor实例
148 |
149 | 先来看下前面DEMO中的初始化`Disruptor`实例代码:
150 |
151 | ```java
152 | // 1,构建disruptor
153 | final Disruptor disruptor = new Disruptor(
154 | new LongEventFactory(),
155 | BUFFER_SIZE,
156 | Executors.newFixedThreadPool(5), // 线程池需要保证足够的线程
157 | ProducerType.SINGLE,
158 | new YieldingWaitStrategy()
159 | );
160 | ```
161 |
162 | 这句代码最终是给`Disruptor`的`ringBuffer`和`executor`属性赋值:
163 |
164 | ```java
165 | // Disruptor.java
166 | public Disruptor(
167 | final EventFactory eventFactory,
168 | final int ringBufferSize,
169 | final Executor executor,
170 | final ProducerType producerType,
171 | final WaitStrategy waitStrategy)
172 | {
173 | this(
174 | // 创建RingBuffer实例
175 | RingBuffer.create(producerType, eventFactory, ringBufferSize, waitStrategy),
176 | executor);
177 | }
178 |
179 | private Disruptor(final RingBuffer ringBuffer, final Executor executor)
180 | {
181 | this.ringBuffer = ringBuffer;
182 | this.executor = executor;
183 | }
184 | ```
185 |
186 | 那么`RingBuffer`实例又是如何创建的呢?我们来看下`RingBuffer.create(producerType, eventFactory, ringBufferSize, waitStrategy)`这句源码:
187 |
188 | ```java
189 | // RingBuffer.java
190 | public static RingBuffer create(
191 | final ProducerType producerType,
192 | final EventFactory factory,
193 | final int bufferSize,
194 | final WaitStrategy waitStrategy)
195 | {
196 | switch (producerType)
197 | {
198 | case SINGLE:
199 | return createSingleProducer(factory, bufferSize, waitStrategy);
200 | case MULTI:
201 | return createMultiProducer(factory, bufferSize, waitStrategy);
202 | default:
203 | throw new IllegalStateException(producerType.toString());
204 | }
205 | }
206 | ```
207 |
208 | 首先会根据`producerType`来创建不同的`Producer`,以创建`SingleProducerSequencer`实例为例进去源码看下:
209 |
210 | ```java
211 | // RingBuffer.java
212 | public static RingBuffer createSingleProducer(
213 | final EventFactory factory,
214 | final int bufferSize,
215 | final WaitStrategy waitStrategy)
216 | {
217 | // 1,创建SingleProducerSequencer实例
218 | SingleProducerSequencer sequencer = new SingleProducerSequencer(bufferSize, waitStrategy);
219 | // 2,创建RingBuffer实例
220 | return new RingBuffer<>(factory, sequencer);
221 | }
222 | ```
223 |
224 | ## 3.1 创建SingleProducerSequencer实例
225 |
226 | 首先创建了`SingleProducerSequencer`实例,给`SingleProducerSequencer`实例的`bufferSize`和`waitStrategy`赋初值;
227 |
228 | ```java
229 | // AbstractSequencer.java
230 | // SingleProducerSequencer父类
231 | public AbstractSequencer(int bufferSize, WaitStrategy waitStrategy)
232 | {
233 | this.bufferSize = bufferSize;
234 | this.waitStrategy = waitStrategy;
235 | }
236 | ```
237 |
238 | 此外,创建`SingleProducerSequencer`实例时还初始化了一个成员变量`cursor`:
239 |
240 | ```java
241 | protected final Sequence cursor = new Sequence(Sequencer.INITIAL_CURSOR_VALUE);
242 | ```
243 |
244 | 即给`cursor`赋值了一个`Sequence`实例对象,`Sequence`是标识`RingBuffer`环形数组的下标,同时生产者和消费者也会同时维护各自的`Sequence`。最重要的是,**`Sequence`通过填充CPU缓存行避免了伪共享带来的性能损耗**,来看下其填充缓存行源码:
245 |
246 | ```java
247 | // Sequence.java
248 | class LhsPadding
249 | {
250 | // 左填充
251 | protected long p1, p2, p3, p4, p5, p6, p7;
252 | }
253 |
254 | class Value extends LhsPadding
255 | {
256 | // Sequence值
257 | protected volatile long value;
258 | }
259 |
260 | class RhsPadding extends Value
261 | {
262 | // 右填充
263 | protected long p9, p10, p11, p12, p13, p14, p15;
264 | }
265 |
266 | public class Sequence extends RhsPadding
267 | {
268 | // ...
269 | }
270 | ```
271 |
272 |
273 |
274 | ## 3.2 创建RingBuffer实例
275 |
276 | 然后核心是创建`RingBuffer`实例,看看最终创建`RingBuffer`实例源码:
277 |
278 | ```java
279 | // RingBuffer.java
280 | RingBufferFields( // RingBufferFields为RingBuffer父类
281 | final EventFactory eventFactory,
282 | final Sequencer sequencer)
283 | {
284 | this.sequencer = sequencer;
285 | this.bufferSize = sequencer.getBufferSize();
286 |
287 | if (bufferSize < 1)
288 | {
289 | throw new IllegalArgumentException("bufferSize must not be less than 1");
290 | }
291 | if (Integer.bitCount(bufferSize) != 1)
292 | {
293 | throw new IllegalArgumentException("bufferSize must be a power of 2");
294 | }
295 |
296 | this.indexMask = bufferSize - 1;
297 | // 【重要特性】内存预加载,内存池机制
298 | this.entries = (E[]) new Object[sequencer.getBufferSize() + 2 * BUFFER_PAD];
299 | fill(eventFactory);
300 | }
301 | ```
302 |
303 | 可以看到先前创建的`SingleProducerSequencer`实例作为构造参数传入给了`RingBuffer`实例的`sequencer`属性赋初值,然后最重要的是在创建`RingBuffer`实例时,会为`RingBuffer`的环形数组提前填充`Event`对象,即**内存池机制**:
304 |
305 | ```java
306 | // RingBuffer.java
307 | private void fill(final EventFactory eventFactory)
308 | {
309 | for (int i = 0; i < bufferSize; i++)
310 | {
311 | entries[BUFFER_PAD + i] = eventFactory.newInstance();
312 | }
313 | }
314 | ```
315 |
316 | 内存池机制好处:
317 |
318 | 1. 提前创建好复用的对象,减少程序运行时因为创建对象而浪费性能,其实也是一种空间换时间的思想;
319 | 2. 因为环形数组对象可复用,从而避免GC来提高性能。
320 |
321 | # 4 构建执行顺序链
322 |
323 | ```java
324 | // 2,构建eventHandler执行链:构建六边形执行顺序
325 | disruptor.handleEventsWith(eventHandler1, eventHandler3);
326 | disruptor.after(eventHandler1).handleEventsWith(eventHandler2);
327 | disruptor.after(eventHandler3).handleEventsWith(eventHandler4);
328 | disruptor.after(eventHandler2, eventHandler4).handleEventsWith(eventHandler5);
329 | ```
330 |
331 | 
332 |
333 | 再来看看`Disruptor`构建执行顺序链相关源码:
334 |
335 | 先来看看`disruptor.handleEventsWith(eventHandler1, eventHandler3);`源码:
336 |
337 | ```java
338 | // Disruptor.java
339 | public final EventHandlerGroup handleEventsWith(final EventHandler super T>... handlers)
340 | {
341 | return createEventProcessors(new Sequence[0], handlers);
342 | }
343 |
344 | EventHandlerGroup createEventProcessors(
345 | final Sequence[] barrierSequences,
346 | final EventHandler super T>[] eventHandlers)
347 | {
348 | checkNotStarted();
349 | // 根据eventHandlers长度来创建多少个消费者Sequence实例,注意这个processorSequences是传递到EventHandlerGroup用于构建执行顺序链用的,
350 | // 比如有执行顺序链:A->B,那么A的sequenct即processorSequences会作为B节点的barrierSequences即dependencySequence
351 | final Sequence[] processorSequences = new Sequence[eventHandlers.length];
352 | // 新建了一个ProcessingSequenceBarrier实例返回
353 | // ProcessingSequenceBarrier实例作用:序号屏障,通过追踪生产者的cursorSequence和每个消费者( EventProcessor)
354 | // 的sequence的方式来协调生产者和消费者之间的数据交换进度
355 | final SequenceBarrier barrier = ringBuffer.newBarrier(barrierSequences);// 如果构建执行顺序链比如A->B,那么barrierSequences是A消费者的sequence;如果是A,C->B,那么barrierSequences是A和C消费者的sequence
356 |
357 | for (int i = 0, eventHandlersLength = eventHandlers.length; i < eventHandlersLength; i++)
358 | {
359 | final EventHandler super T> eventHandler = eventHandlers[i];
360 | // 有多少个eventHandlers就创建多少个BatchEventProcessor实例(消费者),
361 | // 但需要注意的是同一批次的每个BatchEventProcessor实例共用同一个SequenceBarrier实例
362 | final BatchEventProcessor batchEventProcessor =
363 | new BatchEventProcessor<>(ringBuffer, barrier, eventHandler);
364 |
365 | if (exceptionHandler != null)
366 | {
367 | batchEventProcessor.setExceptionHandler(exceptionHandler);
368 | }
369 | // 将batchEventProcessor, eventHandler, barrier封装成EventProcessorInfo实例并加入到ConsumerRepository相关集合
370 | // ConsumerRepository作用:提供存储机制关联EventHandlers和EventProcessors
371 | consumerRepository.add(batchEventProcessor, eventHandler, barrier); // // 如果构建执行顺序链比如A->B,那么B消费者也一样会加入consumerRepository的相关集合
372 | // 获取到每个消费的消费sequece并赋值给processorSequences数组
373 | // 即processorSequences[i]引用了BatchEventProcessor的sequence实例,
374 | // 但processorSequences[i]又是构建生产者gatingSequence和消费者执行器链dependentSequence的来源
375 | processorSequences[i] = batchEventProcessor.getSequence();
376 | }
377 | // 总是拿执行器链最后一个消费者的sequence作为生产者的gateingSequence
378 | updateGatingSequencesForNextInChain(barrierSequences, processorSequences);
379 | // 最终返回封装了Disruptor、ConsumerRepository和消费者sequence数组processorSequences的EventHandlerGroup对象实例返回
380 | return new EventHandlerGroup<>(this, consumerRepository, processorSequences);
381 | }
382 | ```
383 |
384 | 构建`Disruptor`执行顺序链的核心逻辑就在这段源码中,我们缕一缕核心逻辑:
385 |
386 | 1. 有多少个`eventHandlers`就创建多少个`BatchEventProcessor`实例(消费者),`BatchEventProcessor`消费者其实就是一个实现`Runnable`接口的线程实例;
387 | 2. 每个`BatchEventProcessor`实例(消费者)拥有前一个消费者的`sequence`作为其`sequenceBarrier`即`dependentSequence`;
388 | 3. 当前消费者的`sequence`通过`EventHandlerGroup`这个载体来传递给下一个消费者作为其`sequenceBarrier`即`dependentSequence`。
389 |
390 | 再来看看`diruptor.after(eventHandler1)`源码:
391 |
392 | ```java
393 | // Disruptor.java
394 | public final EventHandlerGroup after(final EventHandler... handlers)
395 | {
396 | // 获取指定的EventHandler的消费者sequence并赋值给sequences数组,
397 | // 然后重新新建一个EventHandlerGroup实例返回(封装了前面的指定的消费者sequence被赋值
398 | // 给了EventHandlerGroup的成员变量数组sequences,用于后面指定执行顺序用)
399 | final Sequence[] sequences = new Sequence[handlers.length];
400 | for (int i = 0, handlersLength = handlers.length; i < handlersLength; i++)
401 | {
402 | sequences[i] = consumerRepository.getSequenceFor(handlers[i]);
403 | }
404 |
405 | return new EventHandlerGroup<>(this, consumerRepository, sequences);
406 | }
407 | ```
408 |
409 | 这段源码做的事情也是将当前消费者`sequence`封装进`EventHandlerGroup`,从而可以通过这个载体来传递给下一个消费者作为其`sequenceBarrier`即`dependentSequence`。
410 |
411 | 最终构建的最终`sequence`依赖关系如下图,看到这个图不禁让我想起`AQS`的线程等待链即CLH锁的变相实现,附上文章链接,有兴趣的读者可以比对理解。[AQS基础——多图详解CLH锁的原理与实现](https://mp.weixin.qq.com/s/xBw7koGuZtqU8imZ9_JzDA)
412 |
413 | 
414 |
415 | # 5 启动Disruptor实例
416 |
417 | ```java
418 | // 3, 启动disruptor即启动线程池线程执行BatchEventProcessor任务
419 | disruptor.start();
420 | ```
421 |
422 | 我们再来看看` disruptor.start()`这句源码:
423 |
424 | ```java
425 | // Disruptor.java
426 | public RingBuffer start()
427 | {
428 | checkOnlyStartedOnce();
429 | // 遍历每一个BatchEventProcessor消费者(线程)实例,并把该消费者线程实例跑起来
430 | for (final ConsumerInfo consumerInfo : consumerRepository)
431 | {
432 | consumerInfo.start(executor);
433 | }
434 |
435 | return ringBuffer;
436 | }
437 | ```
438 |
439 | 其实这里做的事情无非就是遍历每个消费者线程实例,然后启动每个消费者线程实例`BatchEventProcessor`,其中`BatchEventProcessor`被封装进`ConsumerInfo`实例。还没生产数据就启动消费线程的话,此时消费者会根据阻塞策略`WaitStrategy`进行阻塞。
440 |
441 | # 6 生产消费数据
442 |
443 | ## 6.1 生产者生产数据
444 |
445 | ```java
446 | // 4,生产者往ringBuffer生产数据并唤醒所有的消费者消费数据
447 | RingBuffer ringBuffer = disruptor.getRingBuffer();
448 | ByteBuffer bb = ByteBuffer.allocate(8);
449 | bb.putLong(0, 666);
450 | ringBuffer.publishEvent(new LongEventTranslatorOneArg(), bb);
451 | ```
452 |
453 | 生产者生产数据的源码在`ringBuffer.publishEvent(new LongEventTranslatorOneArg(), bb);`中。
454 |
455 | ```java
456 | // RingBuffer.java
457 | public void publishEvent(final EventTranslatorOneArg translator, final A arg0)
458 | {
459 | // 【1】获取下一个RingBuffer中需填充数据的event对象的序号,对应生产者
460 | final long sequence = sequencer.next();
461 | // 【2】转换数据格式并生产数据并唤醒消费者
462 | translateAndPublish(translator, sequence, arg0);
463 | }
464 | ```
465 |
466 | ### 6.1.1 生产者获取RingBuffer的sequence
467 |
468 | 先来看下单生产者获取`sequence`的源码:
469 |
470 | ```java
471 | // SingleProducerSequencer.java
472 | public long next(final int n)
473 | {
474 | if (n < 1 || n > bufferSize)
475 | {
476 | throw new IllegalArgumentException("n must be > 0 and < bufferSize");
477 | }
478 | // 总是拿到生产者已生产的当前序号
479 | long nextValue = this.nextValue;
480 | // 获取要生产的下n个序号
481 | long nextSequence = nextValue + n;
482 | // 生产者总是先有bufferSize个坑可以填,所以nextSequence - bufferSize
483 | long wrapPoint = nextSequence - bufferSize;
484 | // 拿到上一次的GatingSequence,因为是缓存,这里不是最新的
485 | long cachedGatingSequence = this.cachedValue;
486 | // 如果生产者生产超过了消费者消费速度,那么这里自旋等待,这里的生产者生产的下标wrapPoint是已经绕了RingBuffer一圈的了哈
487 | if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue)
488 | {
489 | cursor.setVolatile(nextValue); // StoreLoad fence
490 |
491 | long minSequence;
492 | // 自旋等待,其中gatingSequences是前面构建执行顺序链时的最后一个消费者的sequence
493 | while (wrapPoint > (minSequence = Util.getMinimumSequence(gatingSequences, nextValue)))
494 | {
495 | LockSupport.parkNanos(1L); // TODO: Use waitStrategy to spin?
496 | }
497 |
498 | this.cachedValue = minSequence;
499 | }
500 | // 将获取的nextSequence赋值给生产者当前值nextValue
501 | this.nextValue = nextSequence;
502 |
503 | return nextSequence;
504 | }
505 | ```
506 |
507 | 这段源码相对较难,我们缕一缕:
508 |
509 | 1. 生产者把第一圈`RingBuffer`的坑填完后,此时生产者进入`RingBuffer`第2圈,如果消费者消费速度过慢,此时生产者很可能会追上消费者,如果追上消费者那么就让生产者自旋等待;
510 |
511 | 2. 第1点的**如果消费者消费速度过慢**,对于构建了一个过滤器链的消费者中,那么指的是哪个消费者呢?指的就是执行器链最后执行的那个消费者,`gatingSequences`就是执行器链最后执行的那个消费者的`sequence`;**这个`gatingSequences`其实就是防止生产者追赶消费者的`sequenceBarrier`**;
512 |
513 | 
514 |
515 | 3. 生产者总是先把第一圈`RingBuffer`填满后,才会考虑追赶消费者的问题,因此才有`wrapPoint > cachedGatingSequence`的评判条件。
516 |
517 | 前面是单生产者获取`sequence`的源码,对于多生产者`MultiProducerSequencer`的源码逻辑也是类似,只不过将生产者当前值`cursor`和`cachedGatingSequence`用了CAS操作而已,防止多线程问题。
518 |
519 |
520 |
521 | ### 6.1.2 生产者生产数据并唤醒消费者
522 |
523 | 再来看看` translateAndPublish(translator, sequence, arg0)`源码:
524 |
525 | ```java
526 | // RingBuffer.java
527 | private void translateAndPublish(final EventTranslatorOneArg translator, final long sequence, final A arg0)
528 | {
529 | try
530 | {
531 | // 【1】将相应数据arg0转换为相应的Eevent数据,其中get(sequence)会从RingBuffer数组对象池中取出一个对象,而非新建
532 | translator.translateTo(get(sequence), sequence, arg0);
533 | }
534 | finally
535 | {
536 | // 【2】发布该序号说明已经生产完毕供消费者使用
537 | sequencer.publish(sequence);
538 | }
539 | }
540 |
541 |
542 |
543 | // SingleProducerSequencer.java
544 | public void publish(final long sequence)
545 | {
546 | // 【1】给生产者cursor游标赋值新的sequence,说明该sequenc对应的对象数据已经填充(生产)完毕
547 | cursor.set(sequence);// 这个cursor即生产者生产时移动的游标,是AbstractSequencer的成员变量
548 | // 【2】根据阻塞策略将所有消费者唤醒
549 | // 注意:这个waitStrategy实例是所有消费者和生产者共同引用的
550 | waitStrategy.signalAllWhenBlocking();
551 | }
552 |
553 | ```
554 |
555 | 生产者生产数据并唤醒消费者的注释已经写得很清楚了,这里需要注意的点:
556 |
557 | 1. `cursor`才是生产者生产数据的当前下标,消费者消费速度有无追赶上生产者就是拿消费者的消费`sequence`跟生产者的`cursor`比较的,因此生产者生产数据完成后需要给`cursor`赋值;
558 | 2. `waitStrategy`策略对象时跟消费者共用的,这样才能线程间实现阻塞唤醒逻辑。
559 |
560 | ## 6.2 消费者消费数据
561 |
562 | 前面第4节启动`Disruptor`实例中讲到,其实就是开启各个消费者实例`BatchEventProcessor`线程,我们看看其`run`方法中的核心逻辑即`processEvents`源码:
563 |
564 | ```java
565 | // BatchEventProcessor.java
566 | private void processEvents()
567 | {
568 | T event = null;
569 | // nextSequence:消费者要消费的下一个序号
570 | long nextSequence = sequence.get() + 1L; // 【重要】每一个消费者都是从0开始消费,各个消费者维护各自的sequence
571 | // 消费者线程一直在while循环中不断获取生产者数据
572 | while (true)
573 | {
574 | try
575 | {
576 | // 拿到当前生产者的生产序号
577 | final long availableSequence = sequenceBarrier.waitFor(nextSequence);
578 | if (batchStartAware != null)
579 | {
580 | batchStartAware.onBatchStart(availableSequence - nextSequence + 1);
581 | }
582 | // 如果消费者要消费的下一个序号小于生产者的当前生产序号,那么消费者则进行消费
583 | // 这里有一个亮点:就是消费者会一直循环消费直至到达当前生产者生产的序号
584 | while (nextSequence <= availableSequence)
585 | {
586 | event = dataProvider.get(nextSequence);
587 | eventHandler.onEvent(event, nextSequence, nextSequence == availableSequence);
588 | nextSequence++;
589 | }
590 | // 消费完后设置当前消费者的消费进度,这点很重要
591 | // 【1】如果当前消费者是执行链的最后一个消费者,那么其sequence则是生产者的gatingSequence,因为生产者就是拿要生产的下一个sequence跟gatingSequence做比较的哈
592 | // 【2】如果当前消费者不是执行器链的最后一个消费者,那么其sequence作为后面消费者的dependentSequence
593 | sequence.set(availableSequence);
594 | }
595 | catch (final TimeoutException e)
596 | {
597 | notifyTimeout(sequence.get());
598 | }
599 | catch (final AlertException ex)
600 | {
601 | if (running.get() != RUNNING)
602 | {
603 | break;
604 | }
605 | }
606 | catch (final Throwable ex)
607 | {
608 | handleEventException(ex, nextSequence, event);
609 | sequence.set(nextSequence);
610 | nextSequence++;
611 | }
612 | }
613 | }
614 | ```
615 |
616 |
617 |
618 | 消费者线程起来后,然后进入死循环,持续不断从生产者处**批量**获取可用的序号,如果获取到可用序号后,那么遍历所有可用序号,然后调用`eventHandler`的`onEvent`方法消费数据,`onEvent`方法写的是消费者的业务逻辑。消费完后再设置当前消费者的消费进度,这点很重要,用于构建`sequenceBarrier`包括`gatingSequence`和`dependentSequence`。
619 |
620 |
621 |
622 | 下面再来看看消费者是怎么获取可用的序号的,继续看`sequenceBarrier.waitFor(nextSequence)`源码:
623 |
624 | ```java
625 | // ProcessingSequenceBarrier.java
626 |
627 | public long waitFor(final long sequence)
628 | throws AlertException, InterruptedException, TimeoutException
629 | {
630 | checkAlert();
631 | // availableSequence:获取生产者生产后可用的序号
632 | // sequence:消费者要消费的下一个序号
633 | // cursorSequence:生产者生产数据时的当前序号
634 | // dependentSequence:第一个消费者即前面不依赖任何消费者的消费者,dependentSequence就是生产者游标;
635 | // 有依赖其他消费者的消费者,dependentSequence就是依赖的消费者的sequence
636 | long availableSequence = waitStrategy.waitFor(sequence, cursorSequence, dependentSequence, this);
637 |
638 | if (availableSequence < sequence)
639 | {
640 | return availableSequence;
641 | }
642 | // 这个主要是针对多生产者的情形
643 | return sequencer.getHighestPublishedSequence(sequence, availableSequence);
644 | }
645 | ```
646 |
647 | 可以看到`ProcessingSequenceBarrier`封装了`WaitStrategy`等待策略实例,此时消费者获取下一批可用序号的逻辑又封装在了`WaitStrategy`的`waitFor`方法中,以`BlockingWaitStrategy`为例来其实现逻辑:
648 |
649 | ```java
650 | // BlockingWaitStrategy.java
651 |
652 | public long waitFor(long sequence, Sequence cursorSequence, Sequence dependentSequence, SequenceBarrier barrier)
653 | throws AlertException, InterruptedException
654 | {
655 | long availableSequence;
656 | // cursorSequence:生产者的序号
657 | // 第一重条件判断:如果消费者消费速度大于生产者生产速度(即消费者要消费的下一个数据已经大于生产者生产的数据时),那么消费者等待一下
658 | if (cursorSequence.get() < sequence)
659 | {
660 | lock.lock();
661 | try
662 | {
663 | while (cursorSequence.get() < sequence)
664 | {
665 | barrier.checkAlert();
666 | processorNotifyCondition.await();
667 | }
668 | }
669 | finally
670 | {
671 | lock.unlock();
672 | }
673 | }
674 | // 第一重条件判断:自旋等待
675 | // 即当前消费者线程要消费的下一个sequence大于其前面执行链路(若有依赖关系)的任何一个消费者最小sequence(dependentSequence.get()),那么这个消费者要自旋等待,
676 | // 直到前面执行链路(若有依赖关系)的任何一个消费者最小sequence(dependentSequence.get())已经大于等于当前消费者的sequence时,说明前面执行链路的消费者已经消费完了
677 | while ((availableSequence = dependentSequence.get()) < sequence)
678 | {
679 | barrier.checkAlert();
680 | ThreadHints.onSpinWait();
681 | }
682 |
683 | return availableSequence;
684 | }
685 | ```
686 |
687 | 可以看到,消费者获取下一批可用消费序号时,此时要经过两重判断:
688 |
689 | 1. 第一重判断:**消费者消费的序号不能超过当前生产者消费当前生产的序号**,否则消费者就阻塞等待;当然,这里因为是`BlockingWaitStrategy`等待策略的实现,如果是其他策略,比如`BusySpinWaitStrategy`和`YieldingWaitStrategy`的话,这里消费者是不会阻塞等待的,而是自旋,因此这也是其无锁化的实现了,但就是很耗CPU而已;
690 | 2. 第二重判断:**消费者消费的序号不能超过其前面依赖的消费消费的序号**,否则其自旋等待。因为这里是消费者等消费者,按理说前面消费者应该会很快处理完,所以不用阻塞等待;但是消费者等待生产者的话,如果生产者没生产数据的话,消费者还是自旋等待的话会比较浪费CPU,所以对于`BlockingWaitStrategy`策略,是阻塞等待了。
691 |
692 | # 7 WaitStrategy等待策略
693 |
694 | 最后,再来看下`WaitStrategy`有哪些实现类:
695 |
696 | 
697 |
698 | 可以看到消费者的`WaitStrategy`等待策略有8种实现类,可以分为有锁和无锁两大类,然后每一种都有其适用的场合,没有最好的`WaitStrategy`等待策略,只有适合自己应用场景的等待策略。因为其源码不是很难,这里不再逐一分析。
699 |
700 |
701 |
702 | > `disruptor`中文源码注释地址:https://github.com/yuanmabiji/disruptor
703 |
--------------------------------------------------------------------------------
/JDK/1 Java是如何实现自己的SPI机制的? JDK源码(一).md:
--------------------------------------------------------------------------------
1 | **注:该源码分析对应JDK版本为1.8**
2 |
3 | # 1 引言
4 |
5 | 这是【源码笔记】的JDK源码解读的第一篇文章,本篇我们来探究Java的SPI机制的相关源码。
6 |
7 | # 2 什么是SPI机制
8 |
9 | 那么,什么是SPI机制呢?
10 |
11 | SPI是Service Provider Interface 的简称,即**服务提供者接口**的意思。根据字面意思我们可能还有点困惑,SPI说白了就是一种扩展机制,我们在相应配置文件中定义好某个接口的实现类,然后再根据这个接口去这个配置文件中加载这个实例类并实例化,其实SPI就是这么一个东西。说到SPI机制,我们最常见的就是Java的SPI机制,此外,还有Dubbo和SpringBoot自定义的SPI机制。
12 |
13 | 有了SPI机制,那么就为一些框架的灵活扩展提供了可能,而不必将框架的一些实现类写死在代码里面。
14 |
15 | 那么,某些框架是如何利用SPI机制来做到灵活扩展的呢?下面举几个栗子来阐述下:
16 |
17 | 1. **JDBC驱动加载案例**:利用Java的SPI机制,我们可以根据不同的数据库厂商来引入不同的JDBC驱动包;
18 | 2. **SpringBoot的SPI机制**:我们可以在`spring.factories`中加上我们自定义的自动配置类,事件监听器或初始化器等;
19 | 3. **Dubbo的SPI机制**:Dubbo更是把SPI机制应用的**淋漓尽致**,Dubbo基本上自身的每个功能点都提供了扩展点,比如提供了集群扩展,路由扩展和负载均衡扩展等差不多接近30个扩展点。如果Dubbo的某个内置实现不符合我们的需求,那么我们只要利用其SPI机制将我们的实现替换掉Dubbo的实现即可。
20 |
21 | 上面的三个栗子先让我们直观感受下某些框架利用SPI机制是如何做到灵活扩展的。
22 |
23 |
24 | # 3 如何使用Java的SPI?
25 |
26 | 我们先来看看如何使用Java自带的SPI。
27 | 先定义一个`Developer`接口
28 |
29 | ```java
30 | // Developer.java
31 |
32 | package com.ymbj.spi;
33 |
34 | public interface Developer {
35 | void sayHi();
36 | }
37 | ```
38 |
39 | 再定义两个`Developer`接口的两个实现类:
40 |
41 | ```java
42 | // JavaDeveloper.java
43 |
44 | package com.ymbj.spi;
45 |
46 | public class JavaDeveloper implements Developer {
47 |
48 | @Override
49 | public void sayHi() {
50 | System.out.println("Hi, I am a Java Developer.");
51 | }
52 | }
53 | ```
54 |
55 | ```java
56 | // PythonDeveloper.java
57 |
58 | package com.ymbj.spi;
59 |
60 | public class PythonDeveloper implements Developer {
61 |
62 | @Override
63 | public void sayHi() {
64 | System.out.println("Hi, I am a Python Developer.");
65 | }
66 | }
67 | ```
68 |
69 | 然后再在项目`resources`目录下新建一个`META-INF/services`文件夹,然后再新建一个以`Developer`接口的全限定名命名的文件,文件内容为:
70 |
71 | ```java
72 | // com.ymbj.spi.Developer文件
73 |
74 | com.ymbj.spi.JavaDeveloper
75 | com.ymbj.spi.PythonDeveloper
76 | ```
77 |
78 | 最后我们再新建一个测试类`JdkSPITest`:
79 |
80 | ```java
81 | // JdkSPITest.java
82 |
83 | public class JdkSPITest {
84 |
85 | @Test
86 | public void testSayHi() throws Exception {
87 | ServiceLoader serviceLoader = ServiceLoader.load(Developer.class);
88 | serviceLoader.forEach(Developer::sayHi);
89 | }
90 | }
91 | ```
92 |
93 | 运行上面那个测试类,运行成功结果如下截图所示:
94 |
95 | 
96 |
97 | 由上面简单的Demo我们知道了如何使用Java的SPI机制来实现扩展点加载,下面推荐一篇文章[JAVA拾遗--关于SPI机制](https://mp.weixin.qq.com/s/Y-PFZwzSORsznJYRfiM3DA),通过这篇文章,相信大家对Java的SPI会有一个比较深刻的理解,特别是JDBC加载驱动这方面。
98 |
99 | # 4 Java的SPI机制的源码解读
100 |
101 | 通过前面扩展`Developer`接口的简单Demo,我们看到Java的SPI机制实现跟`ServiceLoader`这个类有关,那么我们先来看下`ServiceLoader`的类结构代码:
102 |
103 | ```java
104 | // ServiceLoader实现了【Iterable】接口
105 | public final class ServiceLoader
106 | implements Iterable{
107 | private static final String PREFIX = "META-INF/services/";
108 | // The class or interface representing the service being loaded
109 | private final Class service;
110 | // The class loader used to locate, load, and instantiate providers
111 | private final ClassLoader loader;
112 | // The access control context taken when the ServiceLoader is created
113 | private final AccessControlContext acc;
114 | // Cached providers, in instantiation order
115 | private LinkedHashMap providers = new LinkedHashMap<>();
116 | // The current lazy-lookup iterator
117 | private LazyIterator lookupIterator;
118 | // 构造方法
119 | private ServiceLoader(Class svc, ClassLoader cl) {
120 | service = Objects.requireNonNull(svc, "Service interface cannot be null");
121 | loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
122 | acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
123 | reload();
124 | }
125 |
126 | // ...暂时省略相关代码
127 |
128 | // ServiceLoader的内部类LazyIterator,实现了【Iterator】接口
129 | // Private inner class implementing fully-lazy provider lookup
130 | private class LazyIterator
131 | implements Iterator{
132 | Class service;
133 | ClassLoader loader;
134 | Enumeration configs = null;
135 | Iterator pending = null;
136 | String nextName = null;
137 |
138 | private LazyIterator(Class service, ClassLoader loader) {
139 | this.service = service;
140 | this.loader = loader;
141 | }
142 | // 覆写Iterator接口的hasNext方法
143 | public boolean hasNext() {
144 | // ...暂时省略相关代码
145 | }
146 | // 覆写Iterator接口的next方法
147 | public S next() {
148 | // ...暂时省略相关代码
149 | }
150 | // 覆写Iterator接口的remove方法
151 | public void remove() {
152 | // ...暂时省略相关代码
153 | }
154 |
155 | }
156 |
157 | // 覆写Iterable接口的iterator方法,返回一个迭代器
158 | public Iterator iterator() {
159 | // ...暂时省略相关代码
160 | }
161 |
162 | // ...暂时省略相关代码
163 |
164 | }
165 | ```
166 |
167 | 可以看到,`ServiceLoader`实现了`Iterable`接口,覆写其`iterator`方法能产生一个迭代器;同时`ServiceLoader`有一个内部类`LazyIterator`,而`LazyIterator`又实现了`Iterator`接口,说明`LazyIterator`是一个迭代器。
168 |
169 | ## 4.1 ServiceLoader.load方法,为加载服务提供者实现类做前期准备
170 |
171 | 那么我们现在开始探究Java的SPI机制的源码,
172 | 先来看`JdkSPITest`的第一句代码`ServiceLoader serviceLoader = ServiceLoader.load(Developer.class);`中的`ServiceLoader.load(Developer.class);`的源码:
173 |
174 | ```java
175 | // ServiceLoader.java
176 |
177 | public static ServiceLoader load(Class service) {
178 | //获取当前线程上下文类加载器
179 | ClassLoader cl = Thread.currentThread().getContextClassLoader();
180 | // 将service接口类和线程上下文类加载器作为参数传入,继续调用load方法
181 | return ServiceLoader.load(service, cl);
182 | }
183 | ```
184 |
185 | 我们再来看下`ServiceLoader.load(service, cl);`方法:
186 |
187 | ```java
188 | // ServiceLoader.java
189 |
190 | public static ServiceLoader load(Class service,
191 | ClassLoader loader)
192 | {
193 | // 将service接口类和线程上下文类加载器作为构造参数,新建了一个ServiceLoader对象
194 | return new ServiceLoader<>(service, loader);
195 | }
196 | ```
197 |
198 | 继续看`new ServiceLoader<>(service, loader);`是如何构建的?
199 |
200 | ```java
201 | // ServiceLoader.java
202 |
203 | private ServiceLoader(Class svc, ClassLoader cl) {
204 | service = Objects.requireNonNull(svc, "Service interface cannot be null");
205 | loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
206 | acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
207 | reload();
208 | }
209 | ```
210 |
211 | 可以看到在构建`ServiceLoader`对象时除了给其成员属性赋值外,还调用了`reload`方法:
212 |
213 | ```java
214 | // ServiceLoader.java
215 |
216 | public void reload() {
217 | providers.clear();
218 | lookupIterator = new LazyIterator(service, loader);
219 | }
220 | ```
221 |
222 | 可以看到在`reload`方法中又新建了一个`LazyIterator`对象,然后赋值给`lookupIterator`。
223 |
224 | ```java
225 | // ServiceLoader$LazyIterator.java
226 |
227 | private LazyIterator(Class service, ClassLoader loader) {
228 | this.service = service;
229 | this.loader = loader;
230 | }
231 | ```
232 |
233 | 可以看到在构建`LazyIterator`对象时,也只是给其成员变量`service`和`loader`属性赋值呀,我们一路源码跟下来,也没有看到去`META-INF/services`文件夹加载`Developer`接口的实现类!这就奇怪了,我们都被`ServiceLoader`的`load`方法名骗了。
234 |
235 | 还记得分析前面的代码时新建了一个`LazyIterator`对象吗?`Lazy`顾名思义是**懒**的意思,`Iterator`就是迭代的意思。我们此时猜测那么`LazyIterator`对象的作用应该就是在迭代的时候再去加载`Developer`接口的实现类了。
236 |
237 | ## 4.2 ServiceLoader.iterator方法,实现服务提供者实现类的懒加载
238 |
239 | 我们现在再来看`JdkSPITest`的第二句代码`serviceLoader.forEach(Developer::sayHi);`,执行这句代码后最终会调用`serviceLoader`的`iterator`方法:
240 |
241 | ```java
242 | // serviceLoader.java
243 |
244 | public Iterator iterator() {
245 | return new Iterator() {
246 |
247 | Iterator> knownProviders
248 | = providers.entrySet().iterator();
249 |
250 | public boolean hasNext() {
251 | if (knownProviders.hasNext())
252 | return true;
253 | // 调用lookupIterator即LazyIterator的hasNext方法
254 | // 可以看到是委托给LazyIterator的hasNext方法来实现
255 | return lookupIterator.hasNext();
256 | }
257 |
258 | public S next() {
259 | if (knownProviders.hasNext())
260 | return knownProviders.next().getValue();
261 | // 调用lookupIterator即LazyIterator的next方法
262 | // 可以看到是委托给LazyIterator的next方法来实现
263 | return lookupIterator.next();
264 | }
265 |
266 | public void remove() {
267 | throw new UnsupportedOperationException();
268 | }
269 |
270 | };
271 | }
272 | ```
273 |
274 | 可以看到调用`serviceLoader`的`iterator`方法会返回一个匿名的迭代器对象,而这个匿名迭代器对象其实相当于一个门面类,其覆写的`hasNext`和`next`方法又分别委托`LazyIterator`的`hasNext`和`next`方法来实现了。
275 |
276 | 我们继续调试,发现接下来会进入`LazyIterator`的`hasNext`方法:
277 |
278 | ```java
279 | // serviceLoader$LazyIterator.java
280 |
281 | public boolean hasNext() {
282 | if (acc == null) {
283 | // 调用hasNextService方法
284 | return hasNextService();
285 | } else {
286 | PrivilegedAction action = new PrivilegedAction() {
287 | public Boolean run() { return hasNextService(); }
288 | };
289 | return AccessController.doPrivileged(action, acc);
290 | }
291 | }
292 | ```
293 |
294 | 继续跟进`hasNextService`方法:
295 |
296 | ```java
297 | // serviceLoader$LazyIterator.java
298 |
299 | private boolean hasNextService() {
300 | if (nextName != null) {
301 | return true;
302 | }
303 | if (configs == null) {
304 | try {
305 | // PREFIX = "META-INF/services/"
306 | // service.getName()即接口的全限定名
307 | // 还记得前面的代码构建LazyIterator对象时已经给其成员属性service赋值吗
308 | String fullName = PREFIX + service.getName();
309 | // 加载META-INF/services/目录下的接口文件中的服务提供者类
310 | if (loader == null)
311 | configs = ClassLoader.getSystemResources(fullName);
312 | else
313 | // 还记得前面的代码构建LazyIterator对象时已经给其成员属性loader赋值吗
314 | configs = loader.getResources(fullName);
315 | } catch (IOException x) {
316 | fail(service, "Error locating configuration files", x);
317 | }
318 | }
319 | while ((pending == null) || !pending.hasNext()) {
320 | if (!configs.hasMoreElements()) {
321 | return false;
322 | }
323 | // 返回META-INF/services/目录下的接口文件中的服务提供者类并赋值给pending属性
324 | pending = parse(service, configs.nextElement());
325 | }
326 | // 然后取出一个全限定名赋值给LazyIterator的成员变量nextName
327 | nextName = pending.next();
328 | return true;
329 | }
330 | ```
331 |
332 | 可以看到在执行`LazyIterator`的`hasNextService`方法时最终将去`META-INF/services/`目录下加载接口文件的内容即加载服务提供者实现类的全限定名,然后取出一个服务提供者实现类的全限定名赋值给`LazyIterator`的成员变量`nextName`。到了这里,我们就明白了`LazyIterator`的作用真的是懒加载,在用到的时候才会去加载。
333 |
334 | > **思考**:为何这里要用懒加载呢?懒加载的思想是怎样的呢?懒加载有啥好处呢?你还能举出其他懒加载的案例吗?
335 |
336 | 同样,执行完`LazyIterator`的`hasNext`方法后,会继续执行`LazyIterator`的`next`方法:
337 |
338 | ```java
339 | // serviceLoader$LazyIterator.java
340 |
341 | public S next() {
342 | if (acc == null) {
343 | // 调用nextService方法
344 | return nextService();
345 | } else {
346 | PrivilegedAction action = new PrivilegedAction() {
347 | public S run() { return nextService(); }
348 | };
349 | return AccessController.doPrivileged(action, acc);
350 | }
351 | }
352 | ```
353 |
354 | 我们继续跟进`nextService`方法:
355 |
356 | ```java
357 | // serviceLoader$LazyIterator.java
358 |
359 | private S nextService() {
360 | if (!hasNextService())
361 | throw new NoSuchElementException();
362 | // 还记得在hasNextService方法中为nextName赋值过服务提供者实现类的全限定名吗
363 | String cn = nextName;
364 | nextName = null;
365 | Class> c = null;
366 | try {
367 | // 【1】去classpath中根据传入的类加载器和服务提供者实现类的全限定名去加载服务提供者实现类
368 | c = Class.forName(cn, false, loader);
369 | } catch (ClassNotFoundException x) {
370 | fail(service,
371 | "Provider " + cn + " not found");
372 | }
373 | if (!service.isAssignableFrom(c)) {
374 | fail(service,
375 | "Provider " + cn + " not a subtype");
376 | }
377 | try {
378 | // 【2】实例化刚才加载的服务提供者实现类,并进行转换
379 | S p = service.cast(c.newInstance());
380 | // 【3】最终将实例化后的服务提供者实现类放进providers集合
381 | providers.put(cn, p);
382 | return p;
383 | } catch (Throwable x) {
384 | fail(service,
385 | "Provider " + cn + " could not be instantiated",
386 | x);
387 | }
388 | throw new Error(); // This cannot happen
389 | }
390 | ```
391 |
392 | 可以看到`LazyIterator`的`nextService`方法最终将实例化之前加载的服务提供者实现类,并放进`providers`集合中,随后再调用服务提供者实现类的方法(比如这里指`JavaDeveloper`的`sayHi`方法)。注意,这里是加载一个服务提供者实现类后,若`main`函数中有调用该服务提供者实现类的方法的话,紧接着会调用其方法;然后继续实例化下一个服务提供者类。
393 |
394 | > **设计模式**:可以看到,Java的SPI机制实现代码中应用了迭代器模式,迭代器模式屏蔽了各种存储对象的内部结构差异,提供一个统一的视图来遍历各个存储对象(存储对象可以为集合,数组等)。`java.util.Iterator`也是迭代器模式的实现:同时Java的各个集合类一般实现了`Iterable`接口,实现了其`iterator`方法从而获得`Iterator`接口的实现类对象(一般为集合内部类),然后再利用`Iterator`对象的实现类的`hasNext`和`next`方法来遍历集合元素。
395 |
396 |
397 | # 5 JDBC驱动加载源码解读
398 | 前面分析了Java的SPI机制的源码实现,现在我们再来看下Java的SPI机制的实际案例的应用。
399 |
400 | 我们都知道,JDBC驱动加载是Java的SPI机制的典型应用案例。JDBC主要提供了一套接口规范,而这套规范的api在java的核心库(`rt.jar`)中实现,而不同的数据库厂商只要编写符合这套JDBC接口规范的驱动代码,那么就可以用Java语言来连接数据库了。
401 |
402 | java的核心库(`rt.jar`)中跟JDBC驱动加载的最核心的接口和类分别是`java.sql.Driver`接口和`java.sql.DriverManager`类,其中`java.sql.Driver`是各个数据库厂商的驱动类要实现的接口,而`DriverManager`是用来管理数据库的驱动类的,值得注意的是`DriverManager`这个类有一个`registeredDrivers`集合属性,用来存储数据库的驱动类。
403 | ```java
404 | // DriverManager.java
405 |
406 | // List of registered JDBC drivers
407 | private final static CopyOnWriteArrayList registeredDrivers = new CopyOnWriteArrayList<>();
408 | ```
409 |
410 | 这里以加载Mysql驱动为例来分析JDBC驱动加载的源码。
411 |
412 | 我们的项目引入`mysql-connector-java`依赖(这里的版本是`5.1.47`)后,那么Mysql的驱动实现类文件如下图所示:
413 |
414 |
415 | 
416 | 可以看到Mysql的驱动包中有两个`Driver`驱动类,分别是`com.mysql.jdbc.Driver`和`com.mysql.fabric.jdbc.FabricMySQLDriver`,默认情况下一般我们只用到前者。
417 |
418 | ## 5.1 利用Java的SPI加载Mysql的驱动类
419 |
420 | 那么接下来我们就来探究下JDBC驱动加载的代码是如何实现的。
421 |
422 | 先来看一下一个简单的JDBC的测试代码:
423 | ```java
424 | // JdbcTest.java
425 |
426 | public class JdbcTest {
427 | public static void main(String[] args) {
428 | Connection connection = null;
429 | Statement statement = null;
430 | ResultSet rs = null;
431 |
432 | try {
433 | // 注意:在JDBC 4.0规范中,这里可以不用再像以前那样编写显式加载数据库的代码了
434 | // Class.forName("com.mysql.jdbc.Driver");
435 | // 获取数据库连接,注意【这里将会加载mysql的驱动包】
436 | /***************【主线,切入点】****************/
437 | connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "123456");
438 | // 创建Statement语句
439 | statement = connection.createStatement();
440 | // 执行查询语句
441 | rs = statement.executeQuery("select * from user");
442 | // 遍历查询结果集
443 | while(rs.next()){
444 | String name = rs.getString("name");
445 | System.out.println(name);
446 | }
447 | } catch(Exception e) {
448 | e.printStackTrace();
449 | } finally {
450 | // ...省略释放资源的代码
451 | }
452 | }
453 | }
454 | ```
455 | 在`JdbcTest`的`main`函数调用`DriverManager`的`getConnection`方法时,此时必然会先执行`DriverManager`类的静态代码块的代码,然后再执行`getConnection`方法,那么先来看下`DriverManager`的静态代码块:
456 | ```java
457 | // DriverManager.java
458 |
459 | static {
460 | // 加载驱动实现类
461 | loadInitialDrivers();
462 | println("JDBC DriverManager initialized");
463 | }
464 | ```
465 | 继续跟进`loadInitialDrivers`的代码:
466 | ```java
467 | // DriverManager.java
468 |
469 | private static void loadInitialDrivers() {
470 | String drivers;
471 | try {
472 | drivers = AccessController.doPrivileged(new PrivilegedAction() {
473 | public String run() {
474 | return System.getProperty("jdbc.drivers");
475 | }
476 | });
477 | } catch (Exception ex) {
478 | drivers = null;
479 | }
480 | AccessController.doPrivileged(new PrivilegedAction() {
481 | public Void run() {
482 | // 来到这里,是不是感觉似曾相识,对,没错,我们在前面的JdkSPITest代码中执行过下面的两句代码
483 | // 这句代码前面已经分析过,这里不会真正加载服务提供者实现类
484 | // 而是实例化一个ServiceLoader对象且实例化一个LazyIterator对象用于懒加载
485 | ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);
486 | // 调用ServiceLoader的iterator方法,在迭代的同时,也会去加载并实例化META-INF/services/java.sql.Driver文件
487 | // 的com.mysql.jdbc.Driver和com.mysql.fabric.jdbc.FabricMySQLDriver两个驱动类
488 | /****************【主线,重点关注】**********************/
489 | Iterator driversIterator = loadedDrivers.iterator();
490 | try{
491 | while(driversIterator.hasNext()) {
492 | driversIterator.next();
493 | }
494 | } catch(Throwable t) {
495 | // Do nothing
496 | }
497 | return null;
498 | }
499 | });
500 |
501 | println("DriverManager.initialize: jdbc.drivers = " + drivers);
502 |
503 | if (drivers == null || drivers.equals("")) {
504 | return;
505 | }
506 | String[] driversList = drivers.split(":");
507 | println("number of Drivers:" + driversList.length);
508 | for (String aDriver : driversList) {
509 | try {
510 | println("DriverManager.Initialize: loading " + aDriver);
511 | Class.forName(aDriver, true,
512 | ClassLoader.getSystemClassLoader());
513 | } catch (Exception ex) {
514 | println("DriverManager.Initialize: load failed: " + ex);
515 | }
516 | }
517 | }
518 | ```
519 | 在上面的代码中,我们可以看到Mysql的驱动类加载主要是利用Java的SPI机制实现的,即利用`ServiceLoader`来实现加载并实例化Mysql的驱动类。
520 |
521 | ## 5.2 注册Mysql的驱动类
522 |
523 | 那么,上面的代码只是Mysql驱动类的加载和实例化,**那么,驱动类又是如何被注册进`DriverManager`的`registeredDrivers`集合的呢?**
524 |
525 | 这时,我们注意到`com.mysql.jdbc.Driver`类里面也有个静态代码块,即实例化该类时肯定会触发该静态代码块代码的执行,那么我们直接看下这个静态代码块做了什么事情:
526 | ```java
527 | // com.mysql.jdbc.Driver.java
528 |
529 | // Register ourselves with the DriverManager
530 | static {
531 | try {
532 | // 将自己注册进DriverManager类的registeredDrivers集合
533 | java.sql.DriverManager.registerDriver(new Driver());
534 | } catch (SQLException E) {
535 | throw new RuntimeException("Can't register driver!");
536 | }
537 | }
538 | ```
539 | 可以看到,原来就是Mysql驱动类`com.mysql.jdbc.Driver`在实例化的时候,利用执行其静态代码块的时机时将自己注册进`DriverManager`的`registeredDrivers`集合中。
540 |
541 | 好,继续跟进`DriverManager`的`registerDriver`方法:
542 | ```java
543 | // DriverManager.java
544 |
545 | public static synchronized void registerDriver(java.sql.Driver driver)
546 | throws SQLException {
547 | // 继续调用registerDriver方法
548 | registerDriver(driver, null);
549 | }
550 |
551 | public static synchronized void registerDriver(java.sql.Driver driver,
552 | DriverAction da)
553 | throws SQLException {
554 |
555 | /* Register the driver if it has not already been added to our list */
556 | if(driver != null) {
557 | // 将driver驱动类实例注册进registeredDrivers集合
558 | registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
559 | } else {
560 | // This is for compatibility with the original DriverManager
561 | throw new NullPointerException();
562 | }
563 | println("registerDriver: " + driver);
564 | }
565 | ```
566 | 分析到了这里,我们就明白了Java的SPI机制是如何加载Mysql的驱动类的并如何将Mysql的驱动类注册进`DriverManager`的`registeredDrivers`集合中的。
567 |
568 | ## 5.3 使用之前注册的Mysql驱动类连接数据库
569 |
570 | **既然Mysql的驱动类已经被注册进来了,那么何时会被用到呢?**
571 |
572 | 我们要连接Mysql数据库,自然需要用到Mysql的驱动类,对吧。此时我们回到JDBC的测试代码`JdbcTest`类的`connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "123456");`这句代码中,看一下`getConnection`的源码:
573 |
574 | ```java
575 | // DriverManager.java
576 |
577 | @CallerSensitive
578 | public static Connection getConnection(String url,
579 | String user, String password) throws SQLException {
580 | java.util.Properties info = new java.util.Properties();
581 |
582 | if (user != null) {
583 | info.put("user", user);
584 | }
585 | if (password != null) {
586 | info.put("password", password);
587 | }
588 | // 继续调用getConnection方法来连接数据库
589 | return (getConnection(url, info, Reflection.getCallerClass()));
590 | }
591 | ```
592 | 继续跟进`getConnection`方法:
593 | ```java
594 | // DriverManager.java
595 |
596 | private static Connection getConnection(
597 | String url, java.util.Properties info, Class> caller) throws SQLException {
598 |
599 | ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
600 | synchronized(DriverManager.class) {
601 | // synchronize loading of the correct classloader.
602 | if (callerCL == null) {
603 | callerCL = Thread.currentThread().getContextClassLoader();
604 | }
605 | }
606 | if(url == null) {
607 | throw new SQLException("The url cannot be null", "08001");
608 | }
609 | println("DriverManager.getConnection(\"" + url + "\")");
610 | // Walk through the loaded registeredDrivers attempting to make a connection.
611 | // Remember the first exception that gets raised so we can reraise it.
612 | SQLException reason = null;
613 | // 遍历registeredDrivers集合,注意之前加载的Mysql驱动类实例被注册进这个集合
614 | for(DriverInfo aDriver : registeredDrivers) {
615 | // If the caller does not have permission to load the driver then
616 | // skip it.
617 | // 判断有无权限
618 | if(isDriverAllowed(aDriver.driver, callerCL)) {
619 | try {
620 | println(" trying " + aDriver.driver.getClass().getName());
621 | // 利用Mysql驱动类来连接数据库
622 | /*************【主线,重点关注】*****************/
623 | Connection con = aDriver.driver.connect(url, info);
624 | // 只要连接上,那么加载的其余驱动类比如FabricMySQLDriver将会忽略,因为下面直接返回了
625 | if (con != null) {
626 | // Success!
627 | println("getConnection returning " + aDriver.driver.getClass().getName());
628 | return (con);
629 | }
630 | } catch (SQLException ex) {
631 | if (reason == null) {
632 | reason = ex;
633 | }
634 | }
635 |
636 | } else {
637 | println(" skipping: " + aDriver.getClass().getName());
638 | }
639 |
640 | }
641 |
642 | // if we got here nobody could connect.
643 | if (reason != null) {
644 | println("getConnection failed: " + reason);
645 | throw reason;
646 | }
647 |
648 | println("getConnection: no suitable driver found for "+ url);
649 | throw new SQLException("No suitable driver found for "+ url, "08001");
650 | }
651 | ```
652 | 可以看到,`DriverManager`的`getConnection`方法会从`registeredDrivers`集合中拿出刚才加载的Mysql驱动类来连接数据库。
653 |
654 | 好了,到了这里,JDBC驱动加载的源码就基本分析完了。
655 | # 6 线程上下文类加载器
656 | 前面基本分析完了JDBC驱动加载的源码,但是还有一个很重要的知识点还没讲解,那就是破坏类加载机制的双亲委派模型的**线程上下文类加载器**。
657 |
658 | 我们都知道,JDBC规范的相关类(比如前面的`java.sql.Driver`和`java.sql.DriverManager`)都是在Jdk的`rt.jar`包下,意味着这些类将由启动类加载器(BootstrapClassLoader)加载;而Mysql的驱动类由外部数据库厂商实现,当驱动类被引进项目时也是位于项目的`classpath`中,此时启动类加载器肯定是不可能加载这些驱动类的呀,此时该怎么办?
659 |
660 | 由于类加载机制的双亲委派模型在这方面的缺陷,因此只能打破双亲委派模型了。因为项目`classpath`中的类是由应用程序类加载器(AppClassLoader)来加载,所以我们可否"逆向"让启动类加载器委托应用程序类加载器去加载这些外部数据库厂商的驱动类呢?如果可以,我们怎样才能做到让启动类加载器委托应用程序类加载器去加载
661 | `classpath`中的类呢?
662 |
663 | 答案肯定是可以的,我们可以将应用程序类加载器设置进线程里面,即线程里面新定义一个类加载器的属性`contextClassLoader`,然后在某个时机将应用程序类加载器设置进线程的`contextClassLoader`这个属性里面,如果没有设置的话,那么默认就是应用程序类加载器。然后启动类加载器去加载`java.sql.Driver`和`java.sql.DriverManager`等类时,同时也会从当前线程中取出`contextClassLoader`即应用程序类加载器去`classpath`中加载外部厂商提供的JDBC驱动类。因此,通过破坏类加载机制的双亲委派模型,利用**线程上下文类加载器**完美的解决了该问题。
664 |
665 | 此时我们再回过头来看下**在加载Mysql驱动时是什么时候获取的线程上下文类加载器呢?**
666 |
667 | 答案就是在`DriverManager`的`loadInitialDrivers`方法调用了`ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);`这句代码,而取出线程上下文类加载器就是在`ServiceLoader`的`load`方法中取出:
668 | ```java
669 |
670 | public static ServiceLoader load(Class service) {
671 | // 取出线程上下文类加载器取出的是contextClassLoader,而contextClassLoader装的应用程序类加载器
672 | ClassLoader cl = Thread.currentThread().getContextClassLoader();
673 | // 把刚才取出的线程上下文类加载器作为参数传入,用于后去加载classpath中的外部厂商提供的驱动类
674 | return ServiceLoader.load(service, cl);
675 | }
676 | ```
677 | 因此,到了这里,我们就明白了线程上下文类加载器在加载JDBC驱动包中充当的作用了。此外,我们应该知道,Java的绝大部分涉及SPI的加载都是利用线程上下文类加载器来完成的,比如JNDI,JCE,JBI等。
678 |
679 | > **扩展**:打破类加载机制的双亲委派模型的还有代码的热部署等,另外,Tomcat的类加载机制也值得一读。
680 |
681 | 注:若有些小伙伴对类加载机制的双亲委派模型不清楚的话,推荐[完全理解双亲委派模型与自定义 ClassLoader](https://mp.weixin.qq.com/s/Hy4OSYI8_s0tlEmMfZr-UQ)这篇文了解下。
682 | # 7 扩展:Dubbo的SPI机制
683 | 前面也讲到Dubbo框架身上处处是SPI机制的应用,可以说处处都是扩展点,真的是把SPI机制应用的淋漓尽致。但是Dubbo没有采用默认的Java的SPI机制,而是自己实现了一套SPI机制。
684 |
685 | 那么,**Dubbo为什么没有采用Java的SPI机制呢?**
686 |
687 | 原因主要有两个:
688 |
689 | 1. Java的SPI机制会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源;
690 | 2. Java的SPI机制没有Ioc和AOP的支持,因此Dubbo用了自己的SPI机制:增加了对扩展点IoC和AOP的支持,一个扩展点可以直接setter注入其它扩展点。
691 |
692 | 由于以上原因,Dubbo自定义了一套SPI机制,用于加载自己的扩展点。关于Dubbo的SPI机制这里不再详述,感兴趣的小伙伴们可以去Dubbo官网看看是如何扩展Dubbo的SPI的?还有其官网也有Duboo的SPI的源码分析文章。
693 | # 8 小结
694 |
695 | 好了,Java的SPI机制就解读到这里了,先将前面的知识点再总结下:
696 | 1. Java的SPI机制的使用;
697 | 2. Java的SPI机制的原理;
698 | 3. JDBC驱动的加载原理;
699 | 4. 线程上下文类加载器在JDBC驱动加载中的作用;
700 | 5. 简述了Duboo的SPI机制。
701 |
702 | **原创不易,帮忙点个赞呗!**
703 |
704 | 由于笔者水平有限,若文中有错误还请指出,谢谢。
705 |
706 | **参考**:
707 |
708 | 1,http://dubbo.apache.org/zh-cn/docs/source_code_guide/dubbo-spi.html
709 |
710 | 2,《深入理解Java虚拟机》
711 |
--------------------------------------------------------------------------------
/JUC/Java是如何实现Future模式的?万字详解!.md:
--------------------------------------------------------------------------------
1 | JDK1.8源码分析项目(中文注释)Github地址:
2 |
3 | https://github.com/yuanmabiji/jdk1.8-sourcecode-blogs
4 | # 1 Future是什么?
5 | 先举个例子,我们平时网购买东西,下单后会生成一个订单号,然后商家会根据这个订单号发货,发货后又有一个快递单号,然后快递公司就会根据这个快递单号将网购东西快递给我们。在这一过程中,这一系列的单号都是我们收货的重要凭证。
6 |
7 | 因此,JDK的Future就类似于我们网购买东西的单号,当我们执行某一耗时的任务时,我们可以另起一个线程异步去执行这个耗时的任务,同时我们可以干点其他事情。当事情干完后我们再根据future这个"单号"去提取耗时任务的执行结果即可。因此Future也是多线程中的一种应用模式。
8 |
9 | > **扩展**: 说起多线程,那么Future又与Thread有什么区别呢?最重要的区别就是Thread是没有返回结果的,而Future模式是有返回结果的。
10 |
11 | # 2 如何使用Future
12 | 前面搞明白了什么是Future,下面我们再来举个简单的例子看看如何使用Future。
13 |
14 | 假如现在我们要打火锅,首先我们要准备两样东西:把水烧开和准备食材。因为烧开水是一个比较漫长的过程(相当于耗时的业务逻辑),因此我们可以一边烧开水(相当于另起一个线程),一边准备火锅食材(主线程),等两者都准备好了我们就可以开始打火锅了。
15 |
16 | ```java
17 | // DaHuoGuo.java
18 |
19 | public class DaHuoGuo {
20 | public static void main(String[] args) throws Exception {
21 | FutureTask futureTask = new FutureTask<>(new Callable() {
22 | @Override
23 | public String call() throws Exception {
24 | System.out.println(Thread.currentThread().getName() + ":" + "开始烧开水...");
25 | // 模拟烧开水耗时
26 | Thread.sleep(2000);
27 | System.out.println(Thread.currentThread().getName() + ":" + "开水已经烧好了...");
28 | return "开水";
29 | }
30 | });
31 |
32 | Thread thread = new Thread(futureTask);
33 | thread.start();
34 |
35 | // do other thing
36 | System.out.println(Thread.currentThread().getName() + ":" + " 此时开启了一个线程执行future的逻辑(烧开水),此时我们可以干点别的事情(比如准备火锅食材)...");
37 | // 模拟准备火锅食材耗时
38 | Thread.sleep(3000);
39 | System.out.println(Thread.currentThread().getName() + ":" + "火锅食材准备好了");
40 | String shicai = "火锅食材";
41 |
42 | // 开水已经稍好,我们取得烧好的开水
43 | String boilWater = futureTask.get();
44 |
45 | System.out.println(Thread.currentThread().getName() + ":" + boilWater + "和" + shicai + "已经准备好,我们可以开始打火锅啦");
46 | }
47 | }
48 | ```
49 | 执行结果如下截图,符合我们的预期:
50 | 
51 |
52 | 从以上代码中可以看到,我们使用Future主要有以下步骤:
53 | 1. 新建一个`Callable`匿名函数实现类对象,我们的业务逻辑在`Callable`的`call`方法中实现,其中Callable的泛型是返回结果类型;
54 | 2. 然后把`Callable`匿名函数对象作为`FutureTask`的构造参数传入,构建一个`futureTask`对象;
55 | 3. 然后再把`futureTask`对象作为`Thread`构造参数传入并开启这个线程执行去执行业务逻辑;
56 | 4. 最后我们调用`futureTask`对象的`get`方法得到业务逻辑执行结果。
57 |
58 | 可以看到跟Future使用有关的JDK类主要有`FutureTask`和`Callable`两个,下面主要对`FutureTask`进行源码分析。
59 | > **扩展**: 还有一种使用`Future`的方式是将`Callable`实现类提交给线程池执行的方式,这里不再介绍,自行百度即可。
60 |
61 | # 3 FutureTask类结构分析
62 | 我们先来看下`FutureTask`的类结构:
63 |
64 | 
65 | 可以看到`FutureTask`实现了`RunnableFuture`接口,而`RunnableFuture`接口又继承了`Future`和`Runnable`接口。因为`FutureTask`间接实现了`Runnable`接口,因此可以作为任务被线程`Thread`执行;此外,**最重要的一点**就是`FutureTask`还间接实现了`Future`接口,因此还可以获得任务执行的结果。下面我们就来简单看看这几个接口的相关`api`。
66 | ```java
67 | // Runnable.java
68 |
69 | @FunctionalInterface
70 | public interface Runnable {
71 | // 执行线程任务
72 | public abstract void run();
73 | }
74 | ```
75 | `Runnable`没啥好说的,相信大家都已经很熟悉了。
76 | ```java
77 | // Future.java
78 |
79 | public interface Future {
80 | /**
81 | * 尝试取消线程任务的执行,分为以下几种情况:
82 | * 1)如果线程任务已经完成或已经被取消或其他原因不能被取消,此时会失败并返回false;
83 | * 2)如果任务还未开始执行,此时执行cancel方法,那么任务将被取消执行,此时返回true;TODO 此时对应任务状态state的哪种状态???不懂!!
84 | * 3)如果任务已经开始执行,那么mayInterruptIfRunning这个参数将决定是否取消任务的执行。
85 | * 这里值得注意的是,cancel(true)实质并不能真正取消线程任务的执行,而是发出一个线程
86 | * 中断的信号,一般需要结合Thread.currentThread().isInterrupted()来使用。
87 | */
88 | boolean cancel(boolean mayInterruptIfRunning);
89 | /**
90 | * 判断任务是否被取消,在执行任务完成前被取消,此时会返回true
91 | */
92 | boolean isCancelled();
93 | /**
94 | * 这个方法不管任务正常停止,异常还是任务被取消,总是返回true。
95 | */
96 | boolean isDone();
97 | /**
98 | * 获取任务执行结果,注意是阻塞等待获取任务执行结果。
99 | */
100 | V get() throws InterruptedException, ExecutionException;
101 | /**
102 | * 获取任务执行结果,注意是阻塞等待获取任务执行结果。
103 | * 只不过在规定的时间内未获取到结果,此时会抛出超时异常
104 | */
105 | V get(long timeout, TimeUnit unit)
106 | throws InterruptedException, ExecutionException, TimeoutException;
107 | }
108 | ```
109 | `Future`接口象征着异步执行任务的结果即执行一个耗时任务完全可以另起一个线程执行,然后此时我们可以去做其他事情,做完其他事情我们再调用`Future.get()`方法获取结果即可,此时若异步任务还没结束,此时会一直阻塞等待,直到异步任务执行完获取到结果。
110 |
111 | ```java
112 | // RunnableFuture.java
113 |
114 | public interface RunnableFuture extends Runnable, Future {
115 | /**
116 | * Sets this Future to the result of its computation
117 | * unless it has been cancelled.
118 | */
119 | void run();
120 | }
121 | ```
122 | `RunnableFuture`是`Future`和`Runnable`接口的组合,即这个接口表示又可以被线程异步执行,因为实现了`Runnable`接口,又可以获得线程异步任务的执行结果,因为实现了`Future`接口。因此解决了`Runnable`异步任务没有返回结果的缺陷。
123 |
124 | 接下来我们来看下`FutureTask`,`FutureTask`实现了`RunnableFuture`接口,因此是`Future`和`Runnable`接口的具体实现类,是一个可被取消的异步线程任务,提供了`Future`的基本实现,即异步任务执行后我们能够获取到异步任务的执行结果,是我们接下来分析的重中之重。`FutureTask`可以包装一个`Callable`和`Runnable`对象,此外,`FutureTask`除了可以被线程执行外,还可以被提交给线程池执行。
125 |
126 | 我们先看下`FutureTask`类的`api`,其中重点方法已经红框框出。
127 |
128 | 
129 | 上图中`FutureTask`的`run`方法是被线程异步执行的方法,`get`方法即是取得异步任务执行结果的方法,还有`cancel`方法是取消任务执行的方法。接下来我们主要对这三个方法进行重点分析。
130 | > **思考**:
131 | 1. `FutureTask`覆写的`run`方法的返回类型依然是`void`,表示没有返回值,那么`FutureTask`的`get`方法又是如何获得返回值的呢?
132 | 2. `FutureTask`的`cancel`方法能真正取消线程异步任务的执行么?什么情况下能取消?
133 |
134 | 因为`FutureTask`异步任务执行结果还跟`Callable`接口有关,因此我们再来看下`Callable`接口:
135 | ```java
136 | // Callable.java
137 |
138 | @FunctionalInterface
139 | public interface Callable {
140 | /**
141 | * Computes a result, or throws an exception if unable to do so.
142 | */
143 | V call() throws Exception;
144 | }
145 | ```
146 | 我们都知道,`Callable`接口和`Runnable`接口都可以被提交给线程池执行,唯一不同的就是`Callable`接口是有返回结果的,其中的泛型`V`就是返回结果,而`Runnable`接口是没有返回结果的。
147 | > **思考**: 一般情况下,`Runnable`接口实现类才能被提交给线程池执行,为何`Callable`接口实现类也可以被提交给线程池执行?想想线程池的`submit`方法内部有对`Callable`做适配么?
148 |
149 | # 4 FutureTask源码分析
150 |
151 | ## 4.1 FutureTask成员变量
152 | 我们首先来看下`FutureTask`的成员变量有哪些,理解这些成员变量对后面的源码分析非常重要。
153 | ```java
154 | // FutureTask.java
155 |
156 | /** 封装的Callable对象,其call方法用来执行异步任务 */
157 | private Callable callable;
158 | /** 在FutureTask里面定义一个成员变量outcome,用来装异步任务的执行结果 */
159 | private Object outcome; // non-volatile, protected by state reads/writes
160 | /** 用来执行callable任务的线程 */
161 | private volatile Thread runner;
162 | /** 线程等待节点,reiber stack的一种实现 */
163 | private volatile WaitNode waiters;
164 | /** 任务执行状态 */
165 | private volatile int state;
166 |
167 | // Unsafe mechanics
168 | private static final sun.misc.Unsafe UNSAFE;
169 | // 对应成员变量state的偏移地址
170 | private static final long stateOffset;
171 | // 对应成员变量runner的偏移地址
172 | private static final long runnerOffset;
173 | // 对应成员变量waiters的偏移地址
174 | private static final long waitersOffset;
175 | ```
176 | 这里我们要重点关注下`FutureTask`的`Callable`成员变量,因为`FutureTask`的异步任务最终是委托给`Callable`去实现的。
177 |
178 | > **思考**:
179 | 1. `FutureTask`的成员变量`runner`,`waiters`和`state`都被`volatile`修饰,我们可以思考下为什么这三个成员变量需要被`volatile`修饰,而其他成员变量又不用呢?`volatile`关键字的作用又是什么呢?
180 | 2. 既然已经定义了成员变量`runner`,`waiters`和`state`了,此时又定义了`stateOffset`,`runnerOffset`和`waitersOffset`变量分别对应`runner`,`waiters`和`state`的偏移地址,为何要多此一举呢?
181 |
182 | 我们再来看看`stateOffset`,`runnerOffset`和`waitersOffset`变量这三个变量的初始化过程:
183 | ```java
184 | // FutureTask.java
185 |
186 | static {
187 | try {
188 | UNSAFE = sun.misc.Unsafe.getUnsafe();
189 | Class> k = FutureTask.class;
190 | stateOffset = UNSAFE.objectFieldOffset
191 | (k.getDeclaredField("state"));
192 | runnerOffset = UNSAFE.objectFieldOffset
193 | (k.getDeclaredField("runner"));
194 | waitersOffset = UNSAFE.objectFieldOffset
195 | (k.getDeclaredField("waiters"));
196 | } catch (Exception e) {
197 | throw new Error(e);
198 | }
199 | }
200 | ```
201 | ## 4.2 FutureTask的状态变化
202 | 前面讲了`FutureTask`的成员变量,有一个表示**状态**的成员变量`state`我们要重点关注下,`state`变量表示任务执行的状态。
203 | ```java
204 | // FutureTask.java
205 |
206 | /** 任务执行状态 */
207 | private volatile int state;
208 | /** 任务新建状态 */
209 | private static final int NEW = 0;
210 | /** 任务正在完成状态,是一个瞬间过渡状态 */
211 | private static final int COMPLETING = 1;
212 | /** 任务正常结束状态 */
213 | private static final int NORMAL = 2;
214 | /** 任务执行异常状态 */
215 | private static final int EXCEPTIONAL = 3;
216 | /** 任务被取消状态,对应cancel(false) */
217 | private static final int CANCELLED = 4;
218 | /** 任务中断状态,是一个瞬间过渡状态 */
219 | private static final int INTERRUPTING = 5;
220 | /** 任务被中断状态,对应cancel(true) */
221 | private static final int INTERRUPTED = 6;
222 | ```
223 | 可以看到任务状态变量`state`有以上7种状态,0-6分别对应着每一种状态。任务状态一开始是`NEW`,然后由`FutureTask`的三个方法`set`,`setException`和`cancel`来设置状态的变化,其中状态变化有以下四种情况:
224 |
225 | 1. `NEW -> COMPLETING -> NORMAL`:这个状态变化表示异步任务的正常结束,其中`COMPLETING`是一个瞬间临时的过渡状态,由`set`方法设置状态的变化;
226 | 2. `NEW -> COMPLETING -> EXCEPTIONAL`:这个状态变化表示异步任务执行过程中抛出异常,由`setException`方法设置状态的变化;
227 | 3. `NEW -> CANCELLED`:这个状态变化表示被取消,即调用了`cancel(false)`,由`cancel`方法来设置状态变化;
228 | 4. `NEW -> INTERRUPTING -> INTERRUPTED`:这个状态变化表示被中断,即调用了`cancel(true)`,由`cancel`方法来设置状态变化。
229 |
230 |
231 |
232 |
233 |
234 | ## 4.3 FutureTask构造函数
235 | `FutureTask`有两个构造函数,我们分别来看看:
236 | ```java
237 | // FutureTask.java
238 |
239 | // 第一个构造函数
240 | public FutureTask(Callable callable) {
241 | if (callable == null)
242 | throw new NullPointerException();
243 | this.callable = callable;
244 | this.state = NEW; // ensure visibility of callable
245 | }
246 | ```
247 | 可以看到,这个构造函数在我们前面举的“打火锅”的例子代码中有用到,就是`Callable`成员变量赋值,在异步执行任务时再调用`Callable.call`方法执行异步任务逻辑。此外,此时给任务状态`state`赋值为`NEW`,表示任务新建状态。
248 |
249 | 我们再来看下`FutureTask`的另外一个构造函数:
250 | ```java
251 | // FutureTask.java
252 |
253 | // 另一个构造函数
254 | public FutureTask(Runnable runnable, V result) {
255 | this.callable = Executors.callable(runnable, result);
256 | this.state = NEW; // ensure visibility of callable
257 | }
258 | ```
259 | 这个构造函数在执行`Executors.callable(runnable, result)`时是通过适配器`RunnableAdapter`来将`Runnable`对象`runnable`转换成`Callable`对象,然后再分别给`callable`和`state`变量赋值。
260 |
261 | **注意**,这里我们需要记住的是`FutureTask`新建时,此时的任务状态`state`是`NEW`就好了。
262 | ## 4.4 FutureTask.run方法,用来执行异步任务
263 | 前面我们有讲到`FutureTask`间接实现了`Runnable`接口,覆写了`Runnable`接口的`run`方法,因此该覆写的`run`方法是提交给线程来执行的,同时,该`run`方法正是执行异步任务逻辑的方法,那么,执行完`run`方法又是如何保存异步任务执行的结果的呢?
264 |
265 | 我们现在着重来分析下`run`方法:
266 | ```java
267 | // FutureTask.java
268 |
269 | public void run() {
270 | // 【1】,为了防止多线程并发执行异步任务,这里需要判断线程满不满足执行异步任务的条件,有以下三种情况:
271 | // 1)若任务状态state为NEW且runner为null,说明还未有线程执行过异步任务,此时满足执行异步任务的条件,
272 | // 此时同时调用CAS方法为成员变量runner设置当前线程的值;
273 | // 2)若任务状态state为NEW且runner不为null,任务状态虽为NEW但runner不为null,说明有线程正在执行异步任务,
274 | // 此时不满足执行异步任务的条件,直接返回;
275 | // 1)若任务状态state不为NEW,此时不管runner是否为null,说明已经有线程执行过异步任务,此时没必要再重新
276 | // 执行一次异步任务,此时不满足执行异步任务的条件;
277 | if (state != NEW ||
278 | !UNSAFE.compareAndSwapObject(this, runnerOffset,
279 | null, Thread.currentThread()))
280 | return;
281 | try {
282 | // 拿到之前构造函数传进来的callable实现类对象,其call方法封装了异步任务执行的逻辑
283 | Callable c = callable;
284 | // 若任务还是新建状态的话,那么就调用异步任务
285 | if (c != null && state == NEW) {
286 | // 异步任务执行结果
287 | V result;
288 | // 异步任务执行成功还是始遍标志
289 | boolean ran;
290 | try {
291 | // 【2】,执行异步任务逻辑,并把执行结果赋值给result
292 | result = c.call();
293 | // 若异步任务执行过程中没有抛出异常,说明异步任务执行成功,此时设置ran标志为true
294 | ran = true;
295 | } catch (Throwable ex) {
296 | result = null;
297 | // 异步任务执行过程抛出异常,此时设置ran标志为false
298 | ran = false;
299 | // 【3】设置异常,里面也设置state状态的变化
300 | setException(ex);
301 | }
302 | // 【3】若异步任务执行成功,此时设置异步任务执行结果,同时也设置状态的变化
303 | if (ran)
304 | set(result);
305 | }
306 | } finally {
307 | // runner must be non-null until state is settled to
308 | // prevent concurrent calls to run()
309 | // 异步任务正在执行过程中,runner一直是非空的,防止并发调用run方法,前面有调用cas方法做判断的
310 | // 在异步任务执行完后,不管是正常结束还是异常结束,此时设置runner为null
311 | runner = null;
312 | // state must be re-read after nulling runner to prevent
313 | // leaked interrupts
314 | // 线程执行异步任务后的任务状态
315 | int s = state;
316 | // 【4】如果执行了cancel(true)方法,此时满足条件,
317 | // 此时调用handlePossibleCancellationInterrupt方法处理中断
318 | if (s >= INTERRUPTING)
319 | handlePossibleCancellationInterrupt(s);
320 | }
321 | }
322 | ```
323 | 可以看到执行异步任务的`run`方法主要分为以下四步来执行:
324 | 1. **判断线程是否满足执行异步任务的条件**:为了防止多线程并发执行异步任务,这里需要判断线程满不满足执行异步任务的条件;
325 | 2. **若满足条件,执行异步任务**:因为异步任务逻辑封装在`Callable.call`方法中,此时直接调用`Callable.call`方法执行异步任务,然后返回执行结果;
326 | 3. **根据异步任务的执行情况做不同的处理**:1) 若异步任务执行正常结束,此时调用`set(result);`来设置任务执行结果;2)若异步任务执行抛出异常,此时调用`setException(ex);`来设置异常,详细分析请见`4.4.1小节`;
327 | 4. **异步任务执行完后的善后处理工作**:不管异步任务执行成功还是失败,若其他线程有调用`FutureTask.cancel(true)`,此时需要调用`handlePossibleCancellationInterrupt`方法处理中断,详细分析请见`4.4.2小节`。
328 |
329 | 这里**值得注意**的是判断线程满不满足执行异步任务条件时,`runner`是否为`null`是调用`UNSAFE`的`CAS`方法`compareAndSwapObject`来判断和设置的,同时`compareAndSwapObject`是通过成员变量`runner`的偏移地址`runnerOffset`来给`runner`赋值的,此外,成员变量`runner`被修饰为`volatile`是在多线程的情况下, 一个线程的`volatile`修饰变量的设值能够立即刷进主存,因此值便可被其他线程可见。
330 |
331 | ### 4.4.1 FutureTask的set和setException方法
332 | 下面我们来看下当异步任务执行正常结束时,此时会调用`set(result);`方法:
333 | ```java
334 | // FutureTask.java
335 |
336 | protected void set(V v) {
337 | // 【1】调用UNSAFE的CAS方法判断任务当前状态是否为NEW,若为NEW,则设置任务状态为COMPLETING
338 | // 【思考】此时任务不能被多线程并发执行,什么情况下会导致任务状态不为NEW?
339 | // 答案是只有在调用了cancel方法的时候,此时任务状态不为NEW,此时什么都不需要做,
340 | // 因此需要调用CAS方法来做判断任务状态是否为NEW
341 | if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
342 | // 【2】将任务执行结果赋值给成员变量outcome
343 | outcome = v;
344 | // 【3】将任务状态设置为NORMAL,表示任务正常结束
345 | UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
346 | // 【4】调用任务执行完成方法,此时会唤醒阻塞的线程,调用done()方法和清空等待线程链表等
347 | finishCompletion();
348 | }
349 | }
350 | ```
351 | 可以看到当异步任务正常执行结束后,且异步任务没有被`cancel`的情况下,此时会做以下事情:将任务执行结果保存到`FutureTask`的成员变量`outcome`中的,赋值结束后会调用`finishCompletion`方法来唤醒阻塞的线程(哪里来的阻塞线程?后面会分析),**值得注意**的是这里对应的任务状态变化是**NEW -> COMPLETING -> NORMAL**。
352 |
353 |
354 | 我们继续来看下当异步任务执行过程中抛出异常,此时会调用`setException(ex);`方法。
355 | ```java
356 | // FutureTask.java
357 |
358 | protected void setException(Throwable t) {
359 | // 【1】调用UNSAFE的CAS方法判断任务当前状态是否为NEW,若为NEW,则设置任务状态为COMPLETING
360 | // 【思考】此时任务不能被多线程并发执行,什么情况下会导致任务状态不为NEW?
361 | // 答案是只有在调用了cancel方法的时候,此时任务状态不为NEW,此时什么都不需要做,
362 | // 因此需要调用CAS方法来做判断任务状态是否为NEW
363 | if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
364 | // 【2】将异常赋值给成员变量outcome
365 | outcome = t;
366 | // 【3】将任务状态设置为EXCEPTIONAL
367 | UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
368 | // 【4】调用任务执行完成方法,此时会唤醒阻塞的线程,调用done()方法和清空等待线程链表等
369 | finishCompletion();
370 | }
371 | }
372 | ```
373 | 可以看到`setException(Throwable t)`的代码逻辑跟前面的`set(V v)`几乎一样,不同的是任务执行过程中抛出异常,此时是将异常保存到`FutureTask`的成员变量`outcome`中,还有,**值得注意**的是这里对应的任务状态变化是**NEW -> COMPLETING -> EXCEPTIONAL**。
374 |
375 | 因为异步任务不管正常还是异常结束,此时都会调用`FutureTask`的`finishCompletion`方法来唤醒唤醒阻塞的线程,这里阻塞的线程是指我们调用`Future.get`方法时若异步任务还未执行完,此时该线程会阻塞。
376 | ```java
377 | // FutureTask.java
378 |
379 | private void finishCompletion() {
380 | // assert state > COMPLETING;
381 | // 取出等待线程链表头节点,判断头节点是否为null
382 | // 1)若线程链表头节点不为空,此时以“后进先出”的顺序(栈)移除等待的线程WaitNode节点
383 | // 2)若线程链表头节点为空,说明还没有线程调用Future.get()方法来获取任务执行结果,固然不用移除
384 | for (WaitNode q; (q = waiters) != null;) {
385 | // 调用UNSAFE的CAS方法将成员变量waiters设置为空
386 | if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
387 | for (;;) {
388 | // 取出WaitNode节点的线程
389 | Thread t = q.thread;
390 | // 若取出的线程不为null,则将该WaitNode节点线程置空,且唤醒正在阻塞的该线程
391 | if (t != null) {
392 | q.thread = null;
393 | //【重要】唤醒正在阻塞的该线程
394 | LockSupport.unpark(t);
395 | }
396 | // 继续取得下一个WaitNode线程节点
397 | WaitNode next = q.next;
398 | // 若没有下一个WaitNode线程节点,说明已经将所有等待的线程唤醒,此时跳出for循环
399 | if (next == null)
400 | break;
401 | // 将已经移除的线程WaitNode节点的next指针置空,此时好被垃圾回收
402 | q.next = null; // unlink to help gc
403 | // 再把下一个WaitNode线程节点置为当前线程WaitNode头节点
404 | q = next;
405 | }
406 | break;
407 | }
408 | }
409 | // 不管任务正常执行还是抛出异常,都会调用done方法
410 | done();
411 | // 因为异步任务已经执行完且结果已经保存到outcome中,因此此时可以将callable对象置空了
412 | callable = null; // to reduce footprint
413 | }
414 | ```
415 | `finishCompletion`方法的作用就是不管异步任务正常还是异常结束,此时都要唤醒且移除线程等待链表的等待线程节点,这个链表实现的是一个是`Treiber stack`,因此唤醒(移除)的顺序是"后进先出"即后面先来的线程先被先唤醒(移除),关于这个线程等待链表是如何成链的,后面再继续分析。
416 |
417 | ### 4.4.2 FutureTask的handlePossibleCancellationInterrupt方法
418 | 在`4.4小节`分析的`run`方法里的最后有一个`finally`块,此时若任务状态`state >= INTERRUPTING`,此时说明有其他线程执行了`cancel(true)`方法,此时需要让出`CPU`执行的时间片段给其他线程执行,我们来看下具体的源码:
419 | ```java
420 | // FutureTask.java
421 |
422 | private void handlePossibleCancellationInterrupt(int s) {
423 | // It is possible for our interrupter to stall before getting a
424 | // chance to interrupt us. Let's spin-wait patiently.
425 | // 当任务状态是INTERRUPTING时,此时让出CPU执行的机会,让其他线程执行
426 | if (s == INTERRUPTING)
427 | while (state == INTERRUPTING)
428 | Thread.yield(); // wait out pending interrupt
429 |
430 | // assert state == INTERRUPTED;
431 |
432 | // We want to clear any interrupt we may have received from
433 | // cancel(true). However, it is permissible to use interrupts
434 | // as an independent mechanism for a task to communicate with
435 | // its caller, and there is no way to clear only the
436 | // cancellation interrupt.
437 | //
438 | // Thread.interrupted();
439 | }
440 | ```
441 | > **思考**: 为啥任务状态是`INTERRUPTING`时,此时就要让出CPU执行的时间片段呢?还有为什么要在义务任务执行后才调用`handlePossibleCancellationInterrupt`方法呢?
442 |
443 | ## 4.5 FutureTask.get方法,获取任务执行结果
444 |
445 | ```java
446 | 前面我们起一个线程在其`run`方法中执行异步任务后,此时我们可以调用`FutureTask.get`方法来获取异步任务执行的结果。
447 |
448 | // FutureTask.java
449 |
450 | public V get() throws InterruptedException, ExecutionException {
451 | int s = state;
452 | // 【1】若任务状态<=COMPLETING,说明任务正在执行过程中,此时可能正常结束,也可能遇到异常
453 | if (s <= COMPLETING)
454 | s = awaitDone(false, 0L);
455 | // 【2】最后根据任务状态来返回任务执行结果,此时有三种情况:1)任务正常执行;2)任务执行异常;3)任务被取消
456 | return report(s);
457 | }
458 | ```
459 | 可以看到,如果任务状态`state<=COMPLETING`,说明异步任务正在执行过程中,此时会调用`awaitDone`方法阻塞等待;当任务执行完后,此时再调用`report`方法来报告任务结果,此时有三种情况:1)任务正常执行;2)任务执行异常;3)任务被取消。
460 |
461 | ### 4.5.1 FutureTask.awaitDone方法
462 | `FutureTask.awaitDone`方法会阻塞获取异步任务执行结果的当前线程,直到异步任务执行完成。
463 | ```java
464 | // FutureTask.java
465 |
466 | private int awaitDone(boolean timed, long nanos)
467 | throws InterruptedException {
468 | // 计算超时结束时间
469 | final long deadline = timed ? System.nanoTime() + nanos : 0L;
470 | // 线程链表头节点
471 | WaitNode q = null;
472 | // 是否入队
473 | boolean queued = false;
474 | // 死循环
475 | for (;;) {
476 | // 如果当前获取任务执行结果的线程被中断,此时移除该线程WaitNode链表节点,并抛出InterruptedException
477 | if (Thread.interrupted()) {
478 | removeWaiter(q);
479 | throw new InterruptedException();
480 | }
481 |
482 | int s = state;
483 | // 【5】如果任务状态>COMPLETING,此时返回任务执行结果,其中此时任务可能正常结束(NORMAL),可能抛出异常(EXCEPTIONAL)
484 | // 或任务被取消(CANCELLED,INTERRUPTING或INTERRUPTED状态的一种)
485 | if (s > COMPLETING) {
486 | // 【问】此时将当前WaitNode节点的线程置空,其中在任务结束时也会调用finishCompletion将WaitNode节点的thread置空,
487 | // 这里为什么又要再调用一次q.thread = null;呢?
488 | // 【答】因为若很多线程来获取任务执行结果,在任务执行完的那一刻,此时获取任务的线程要么已经在线程等待链表中,要么
489 | // 此时还是一个孤立的WaitNode节点。在线程等待链表中的的所有WaitNode节点将由finishCompletion来移除(同时唤醒)所有
490 | // 等待的WaitNode节点,以便垃圾回收;而孤立的线程WaitNode节点此时还未阻塞,因此不需要被唤醒,此时只要把其属性置为
491 | // null,然后其有没有被谁引用,因此可以被GC。
492 | if (q != null)
493 | q.thread = null;
494 | // 【重要】返回任务执行结果
495 | return s;
496 | }
497 | // 【4】若任务状态为COMPLETING,此时说明任务正在执行过程中,此时获取任务结果的线程需让出CPU执行时间片段
498 | else if (s == COMPLETING) // cannot time out yet
499 | Thread.yield();
500 | // 【1】若当前线程还没有进入线程等待链表的WaitNode节点,此时新建一个WaitNode节点,并把当前线程赋值给WaitNode节点的thread属性
501 | else if (q == null)
502 | q = new WaitNode();
503 | // 【2】若当前线程等待节点还未入线程等待队列,此时加入到该线程等待队列的头部
504 | else if (!queued)
505 | queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
506 | q.next = waiters, q);
507 | // 若有超时设置,那么处理超时获取任务结果的逻辑
508 | else if (timed) {
509 | nanos = deadline - System.nanoTime();
510 | if (nanos <= 0L) {
511 | removeWaiter(q);
512 | return state;
513 | }
514 | LockSupport.parkNanos(this, nanos);
515 | }
516 | // 【3】若没有超时设置,此时直接阻塞当前线程
517 | else
518 | LockSupport.park(this);
519 | }
520 | }
521 | ```
522 | `FutureTask.awaitDone`方法主要做的事情总结如下:
523 |
524 | 0. 首先`awaitDone`方法里面是一个死循环;
525 | 1. 若获取结果的当前线程被其他线程中断,此时移除该线程WaitNode链表节点,并抛出InterruptedException;
526 | 2. 如果任务状态`state>COMPLETING`,此时返回任务执行结果;
527 | 3. 若任务状态为`COMPLETING`,此时获取任务结果的线程需让出CPU执行时间片段;
528 | 4. 若`q == null`,说明当前线程还未设置到`WaitNode`节点,此时新建`WaitNode`节点并设置其`thread`属性为当前线程;
529 | 5. 若`queued==false`,说明当前线程`WaitNode`节点还未加入线程等待链表,此时加入该链表的头部;
530 | 6. 当`timed`设置为true时,此时该方法具有超时功能,关于超时的逻辑这里不详细分析;
531 | 7. 当前面6个条件都不满足时,此时阻塞当前线程。
532 |
533 | 我们分析到这里,可以直到执行异步任务只能有一个线程来执行,而获取异步任务结果可以多线程来获取,当异步任务还未执行完时,此时获取异步任务结果的线程会加入线程等待链表中,然后调用调用`LockSupport.park(this);`方法阻塞当前线程。直到异步任务执行完成,此时会调用`finishCompletion`方法来唤醒并移除线程等待链表的每个`WaitNode`节点,这里这里唤醒(移除)`WaitNode`节点的线程是从链表头部开始的,前面我们也已经分析过。
534 |
535 | 还有一个特别需要注意的就是`awaitDone`方法里面是一个死循环,当一个获取异步任务的线程进来后可能会多次进入多个条件分支执行不同的业务逻辑,也可能只进入一个条件分支。下面分别举两种可能的情况进行说明:
536 |
537 | **情况1**:
538 | 当获取异步任务结果的线程进来时,此时异步任务还未执行完即`state=NEW`且没有超时设置时:
539 | 1. **第一次循环**:此时`q = null`,此时进入上面代码标号`【1】`的判断分支,即为当前线程新建一个`WaitNode`节点;
540 | 2. **第二次循环**:此时`queued = false`,此时进入上面代码标号`【2】`的判断分支,即将之前新建的`WaitNode`节点加入线程等待链表中;
541 | 3. **第三次循环**:此时进入上面代码标号`【3】`的判断分支,即阻塞当前线程;
542 | 4. **第四次循环**:加入此时异步任务已经执行完,此时进入上面代码标号`【5】`的判断分支,即返回异步任务执行结果。
543 |
544 | **情况2**:
545 | 当获取异步任务结果的线程进来时,此时异步任务已经执行完即`state>COMPLETING`且没有超时设置时,此时直接进入上面代码标号`【5】`的判断分支,即直接返回异步任务执行结果即可,也不用加入线程等待链表了。
546 | ### 4.5.2 FutureTask.report方法
547 | 在`get`方法中,当异步任务执行结束后即不管异步任务正常还是异常结束,亦或是被`cancel`,此时获取异步任务结果的线程都会被唤醒,因此会继续执行`FutureTask.report`方法报告异步任务的执行情况,此时可能会返回结果,也可能会抛出异常。
548 | ```java
549 | // FutureTask.java
550 |
551 | private V report(int s) throws ExecutionException {
552 | // 将异步任务执行结果赋值给x,此时FutureTask的成员变量outcome要么保存着
553 | // 异步任务正常执行的结果,要么保存着异步任务执行过程中抛出的异常
554 | Object x = outcome;
555 | // 【1】若异步任务正常执行结束,此时返回异步任务执行结果即可
556 | if (s == NORMAL)
557 | return (V)x;
558 | // 【2】若异步任务执行过程中,其他线程执行过cancel方法,此时抛出CancellationException异常
559 | if (s >= CANCELLED)
560 | throw new CancellationException();
561 | // 【3】若异步任务执行过程中,抛出异常,此时将该异常转换成ExecutionException后,重新抛出。
562 | throw new ExecutionException((Throwable)x);
563 | }
564 | ```
565 | ## 4.6 FutureTask.cancel方法,取消执行任务
566 | 我们最后再来看下`FutureTask.cancel`方法,我们一看到`FutureTask.cancel`方法,肯定一开始就天真的认为这是一个可以取消异步任务执行的方法,如果我们这样认为的话,只能说我们猜对了一半。
567 | ```java
568 | // FutureTask.java
569 |
570 | public boolean cancel(boolean mayInterruptIfRunning) {
571 | // 【1】判断当前任务状态,若state == NEW时根据mayInterruptIfRunning参数值给当前任务状态赋值为INTERRUPTING或CANCELLED
572 | // a)当任务状态不为NEW时,说明异步任务已经完成,或抛出异常,或已经被取消,此时直接返回false。
573 | // TODO 【问题】此时若state = COMPLETING呢?此时为何也直接返回false,而不能发出中断异步任务线程的中断信号呢??
574 | // TODO 仅仅因为COMPLETING是一个瞬时态吗???
575 | // b)当前仅当任务状态为NEW时,此时若mayInterruptIfRunning为true,此时任务状态赋值为INTERRUPTING;否则赋值为CANCELLED。
576 | if (!(state == NEW &&
577 | UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
578 | mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
579 | return false;
580 | try { // in case call to interrupt throws exception
581 | // 【2】如果mayInterruptIfRunning为true,此时中断执行异步任务的线程runner(还记得执行异步任务时就把执行异步任务的线程就赋值给了runner成员变量吗)
582 | if (mayInterruptIfRunning) {
583 | try {
584 | Thread t = runner;
585 | if (t != null)
586 | // 中断执行异步任务的线程runner
587 | t.interrupt();
588 | } finally { // final state
589 | // 最后任务状态赋值为INTERRUPTED
590 | UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
591 | }
592 | }
593 | // 【3】不管mayInterruptIfRunning为true还是false,此时都要调用finishCompletion方法唤醒阻塞的获取异步任务结果的线程并移除线程等待链表节点
594 | } finally {
595 | finishCompletion();
596 | }
597 | // 返回true
598 | return true;
599 | }
600 | ```
601 | 以上代码中,当异步任务状态`state != NEW`时,说明异步任务已经正常执行完或已经异常结束亦或已经被`cancel`,此时直接返回`false`;当异步任务状态`state = NEW`时,此时又根据`mayInterruptIfRunning`参数是否为`true`分为以下两种情况:
602 |
603 | 1. 当`mayInterruptIfRunning = false`时,此时任务状态`state`直接被赋值为`CANCELLED`,此时不会对执行异步任务的线程发出中断信号,**值得注意**的是这里对应的任务状态变化是**NEW -> CANCELLED**。
604 | 2. 当`mayInterruptIfRunning = true`时,此时会对执行异步任务的线程发出中断信号,**值得注意**的是这里对应的任务状态变化是**NEW -> INTERRUPTING -> INTERRUPTED**。
605 |
606 | 最后不管`mayInterruptIfRunning`为`true`还是`false`,此时都要调用`finishCompletion`方法唤醒阻塞的获取异步任务结果的线程并移除线程等待链表节点。
607 |
608 | 从`FutureTask.cancel`源码中我们可以得出答案,该方法并不能真正中断正在执行异步任务的线程,只能对执行异步任务的线程发出中断信号。如果执行异步任务的线程处于`sleep`、`wait`或`join`的状态中,此时会抛出`InterruptedException`异常,该线程可以被中断;此外,如果异步任务需要在`while`循环执行的话,此时可以结合以下代码来结束异步任务线程,即执行异步任务的线程被中断时,此时`Thread.currentThread().isInterrupted()`返回`true`,不满足`while`循环条件因此退出循环,结束异步任务执行线程,如下代码:
609 | ```
610 | public Integer call() throws Exception {
611 | while (!Thread.currentThread().isInterrupted()) {
612 | // 业务逻辑代码
613 | System.out.println("running...");
614 |
615 | }
616 | return 666;
617 | }
618 | ```
619 |
620 | **注意**:调用了`FutureTask.cancel`方法,只要返回结果是`true`,假如异步任务线程虽然不能被中断,即使异步任务线程正常执行完毕,返回了执行结果,此时调用`FutureTask.get`方法也不能够获取异步任务执行结果,此时会抛出`CancellationException`异常。请问知道这是为什么吗?
621 |
622 | 因为调用了`FutureTask.cancel`方法,只要返回结果是`true`,此时的任务状态为`CANCELLED`或`INTERRUPTED`,同时必然会执行`finishCompletion`方法,而`finishCompletion`方法会唤醒获取异步任务结果的线程等待列表的线程,而获取异步任务结果的线程唤醒后发现状态`s >= CANCELLED`,此时就会抛出`CancellationException`异常了。
623 |
624 | # 5 总结
625 |
626 | 好了,本篇文章对`FutureTask`的源码分析就到此结束了,下面我们再总结下`FutureTask`的实现逻辑:
627 | 1. 我们实现`Callable`接口,在覆写的`call`方法中定义需要执行的业务逻辑;
628 | 2. 然后把我们实现的`Callable`接口实现对象传给`FutureTask`,然后`FutureTask`作为异步任务提交给线程执行;
629 | 3. 最重要的是`FutureTask`内部维护了一个状态`state`,任何操作(异步任务正常结束与否还是被取消)都是围绕着这个状态进行,并随时更新`state`任务的状态;
630 | 4. 只能有一个线程执行异步任务,当异步任务执行结束后,此时可能正常结束,异常结束或被取消。
631 | 5. 可以多个线程并发获取异步任务执行结果,当异步任务还未执行完,此时获取异步任务的线程将加入线程等待列表进行等待;
632 | 6. 当异步任务线程执行结束后,此时会唤醒获取异步任务执行结果的线程,注意唤醒顺序是"后进先出"即后面加入的阻塞线程先被唤醒。
633 | 7. 当我们调用`FutureTask.cancel`方法时并不能真正停止执行异步任务的线程,只是发出中断线程的信号。但是只要`cancel`方法返回`true`,此时即使异步任务能正常执行完,此时我们调用`get`方法获取结果时依然会抛出`CancellationException`异常。
634 |
635 | > **扩展**: 前面我们提到了`FutureTask`的`runner`,`waiters`和`state`都是用`volatile`关键字修饰,说明这三个变量都是多线程共享的对象(成员变量),会被多线程操作,此时用`volatile`关键字修饰是为了一个线程操作`volatile`属性变量值后,能够及时对其他线程可见。此时多线程操作成员变量仅仅用了`volatile`关键字仍然会有线程安全问题的,而此时Doug Lea老爷子没有引入任何线程锁,而是采用了`Unsafe`的`CAS`方法来代替锁操作,确保线程安全性。
636 |
637 | # 6 分析FutureTask源码,我们能学到什么?
638 |
639 | 我们分析源码的目的是什么?除了弄懂`FutureTask`的内部实现原理外,我们还要借鉴大佬写写框架源码的各种技巧,只有这样,我们才能成长。
640 |
641 | 分析了`FutureTask`源码,我们可以从中学到:
642 | 1. 利用`LockSupport`来实现线程的阻塞\唤醒机制;
643 | 2. 利用`volatile`和`UNSAFE`的`CAS`方法来实现线程共享变量的无锁化操作;
644 | 3. 若要编写超时异常的逻辑可以参考`FutureTask`的`get(long timeout, TimeUnit unit)`的实现逻辑;
645 | 4. 多线程获取某一成员变量结果时若需要等待时的线程等待链表的逻辑实现;
646 | 5. 某一异步任务在某一时刻只能由单一线程执行的逻辑实现;
647 | 6. `FutureTask`中的任务状态`satate`的变化处理的逻辑实现。
648 | 7. ...
649 |
650 | 以上列举的几点都是我们可以学习参考的地方。
651 |
652 |
653 | **若您觉得不错,请无情的转发和点赞吧!**
654 |
655 | 【源码笔记】Github地址:
656 |
657 | https://github.com/yuanmabiji/Java-SourceCode-Blogs
658 |
659 | -------------------------------------------------------------------------------
660 | 公众号【源码笔记】,专注于Java后端系列框架的源码分析。
661 |
662 | 
--------------------------------------------------------------------------------