diff --git "a/02.\347\272\277\347\250\213\347\232\204\347\212\266\346\200\201\350\275\254\346\215\242\344\273\245\345\217\212\345\237\272\346\234\254\346\223\215\344\275\234/\347\272\277\347\250\213\347\212\266\346\200\201\350\275\254\346\215\242\344\273\245\345\217\212\345\237\272\346\234\254\346\223\215\344\275\234.md" "b/02.\347\272\277\347\250\213\347\232\204\347\212\266\346\200\201\350\275\254\346\215\242\344\273\245\345\217\212\345\237\272\346\234\254\346\223\215\344\275\234/\347\272\277\347\250\213\347\212\266\346\200\201\350\275\254\346\215\242\344\273\245\345\217\212\345\237\272\346\234\254\346\223\215\344\275\234.md" index 7458d85..8f43614 100644 --- "a/02.\347\272\277\347\250\213\347\232\204\347\212\266\346\200\201\350\275\254\346\215\242\344\273\245\345\217\212\345\237\272\346\234\254\346\223\215\344\275\234/\347\272\277\347\250\213\347\212\266\346\200\201\350\275\254\346\215\242\344\273\245\345\217\212\345\237\272\346\234\254\346\223\215\344\275\234.md" +++ "b/02.\347\272\277\347\250\213\347\232\204\347\212\266\346\200\201\350\275\254\346\215\242\344\273\245\345\217\212\345\237\272\346\234\254\346\223\215\344\275\234/\347\272\277\347\250\213\347\212\266\346\200\201\350\275\254\346\215\242\344\273\245\345\217\212\345\237\272\346\234\254\346\223\215\344\275\234.md" @@ -52,7 +52,7 @@ - 由于java不能多继承可以实现多个接口,因此,在创建线程的时候尽量多考虑采用实现接口的形式; - 实现callable接口,提交给ExecutorService返回的是异步执行的结果,另外,通常也可以利用FutureTask(Callable callable)将callable进行包装然后FeatureTask提交给ExecutorsService。如图, -![FutureTask接口实现关系](https://github.com/CL0610/Java-concurrency/blob/master/2.%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E4%BB%A5%E5%8F%8A%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C/futureTask%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E5%85%B3%E7%B3%BB.png) +![FutureTask接口实现关系](https://github.com/hexiangtao/Java-concurrency/blob/master/02.%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E4%BB%A5%E5%8F%8A%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C/futureTask%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E5%85%B3%E7%B3%BB.png) 另外由于FeatureTask也实现了Runable接口也可以利用上面第二种方式(实现Runable接口)来新建线程; @@ -61,7 +61,7 @@ # 2. 线程状态转换 # -![线程状态转换图](https://github.com/CL0610/Java-concurrency/blob/master/2.%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E4%BB%A5%E5%8F%8A%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C/%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E5%85%B3%E7%B3%BB.png) +![线程状态转换图](https://github.com/hexiangtao/Java-concurrency/blob/master/02.%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E4%BB%A5%E5%8F%8A%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C/%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81.png) @@ -71,7 +71,7 @@ 用一个表格将上面六种状态进行一个总结归纳。 -![JAVA线程的状态](https://github.com/CL0610/Java-concurrency/blob/master/2.%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E4%BB%A5%E5%8F%8A%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C/%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81.png) +![JAVA线程的状态](https://github.com/hexiangtao/Java-concurrency/blob/master/02.%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E4%BB%A5%E5%8F%8A%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C/%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81.png) # 3. 线程状态的基本操作 # @@ -83,7 +83,7 @@ isInterrupted()来感知其他线程对其自身的中断操作,从而做出响应。另外,同样可以调用Thread的静态方法 interrupted()对当前线程进行中断操作,该方法会清除中断标志位。**需要注意的是,当抛出InterruptedException时候,会清除中断标志位,也就是说在调用isInterrupted会返回false。** -![线程中断的方法](https://github.com/CL0610/Java-concurrency/blob/master/2.%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E4%BB%A5%E5%8F%8A%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C/%E4%B8%AD%E6%96%AD%E7%BA%BF%E7%A8%8B%E6%96%B9%E6%B3%95.png) +![线程中断的方法](hhttps://github.com/hexiangtao/Java-concurrency/blob/master/02.%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E4%BB%A5%E5%8F%8A%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C/%E4%B8%AD%E6%96%AD%E7%BA%BF%E7%A8%8B%E6%96%B9%E6%B3%95.png) diff --git "a/05.\345\275\273\345\272\225\347\220\206\350\247\243volatile/java\345\205\263\351\224\256\345\255\227---volatile.md" "b/05.\345\275\273\345\272\225\347\220\206\350\247\243volatile/java\345\205\263\351\224\256\345\255\227---volatile.md" index 4d62eaf..88a15cd 100644 --- "a/05.\345\275\273\345\272\225\347\220\206\350\247\243volatile/java\345\205\263\351\224\256\345\255\227---volatile.md" +++ "b/05.\345\275\273\345\272\225\347\220\206\350\247\243volatile/java\345\205\263\351\224\256\345\255\227---volatile.md" @@ -46,19 +46,19 @@ volatile是怎样实现了?比如一个很简单的Java代码: 上面的实例代码对应的happens-before关系如下图所示: -![VolatileExample的happens-before关系推导](http://upload-images.jianshu.io/upload_images/2615789-c9c291d6c0b3e0f1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![VolatileExample的happens-before关系推导](https://github.com/hexiangtao/Java-concurrency/blob/master/05.%E5%BD%BB%E5%BA%95%E7%90%86%E8%A7%A3volatile/VolatileExample%E7%9A%84happens-before%E5%85%B3%E7%B3%BB%E6%8E%A8%E5%AF%BC.png) 加锁线程A先执行writer方法,然后线程B执行reader方法图中每一个箭头两个节点就代码一个happens-before关系,黑色的代表根据**程序顺序规则**推导出来,红色的是根据**volatile变量的写happens-before 于任意后续对volatile变量的读**,而蓝色的就是根据传递性规则推导出来的。这里的2 happen-before 3,同样根据happens-before规则定义:如果A happens-before B,则A的执行结果对B可见,并且A的执行顺序先于B的执行顺序,我们可以知道操作2执行结果对操作3来说是可见的,也就是说当线程A将volatile变量 flag更改为true后线程B就能够迅速感知。 # 4. volatile的内存语义 # 还是按照**两个核心**的分析方式,分析完happens-before关系后我们现在就来进一步分析volatile的内存语义(按照这种方式去学习,会不会让大家对知识能够把握的更深,而不至于不知所措,如果大家认同我的这种方式,不妨给个赞,小弟在此谢过,对我是个鼓励)。还是以上面的代码为例,假设线程A先执行writer方法,线程B随后执行reader方法,初始时线程的本地内存中flag和a都是初始状态,下图是线程A执行volatile写后的状态图。 -![线程A执行volatile写后的内存状态图](http://upload-images.jianshu.io/upload_images/2615789-9e5098f09d5ad065.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![线程A执行volatile写后的内存状态图](https://github.com/hexiangtao/Java-concurrency/blob/master/05.%E5%BD%BB%E5%BA%95%E7%90%86%E8%A7%A3volatile/%E7%BA%BF%E7%A8%8BA%E6%89%A7%E8%A1%8Cvolatile%E5%86%99%E5%90%8E%E7%9A%84%E5%86%85%E5%AD%98%E7%8A%B6%E6%80%81%E5%9B%BE.png 当volatile变量写后,线程中本地内存中共享变量就会置为失效的状态,因此线程B再需要读取从主内存中去读取该变量的最新值。下图就展示了线程B读取同一个volatile变量的内存变化示意图。 -![线程B读volatile后的内存状态图](http://upload-images.jianshu.io/upload_images/2615789-606771789255958f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![线程B读volatile后的内存状态图](https://github.com/hexiangtao/Java-concurrency/blob/master/05.%E5%BD%BB%E5%BA%95%E7%90%86%E8%A7%A3volatile/%E7%BA%BF%E7%A8%8BB%E8%AF%BBvolatile%E5%90%8E%E7%9A%84%E5%86%85%E5%AD%98%E7%8A%B6%E6%80%81%E5%9B%BE.png) 从横向来看,线程A和线程B之间进行了一次通信,线程A在写volatile变量时,实际上就像是给B发送了一个消息告诉线程B你现在的值都是旧的了,然后线程B读这个volatile变量时就像是接收了线程A刚刚发送的消息。既然是旧的了,那线程B该怎么办了?自然而然就只能去主内存去取啦。 @@ -72,7 +72,7 @@ volatile是怎样实现了?比如一个很简单的Java代码: JMM内存屏障分为四类见下图, -![内存屏障分类表](http://upload-images.jianshu.io/upload_images/2615789-27cf04634cbdf284.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/680) +![内存屏障分类表](https://github.com/hexiangtao/Java-concurrency/blob/master/05.%E5%BD%BB%E5%BA%95%E7%90%86%E8%A7%A3volatile/%E5%86%85%E5%AD%98%E5%B1%8F%E9%9A%9C%E5%88%86%E7%B1%BB%E8%A1%A8.png) java编译器会在生成指令系列时在适当的位置会插入内存屏障指令来禁止特定类型的处理器重排序。为了实现volatile的内存语义,JMM会限制特定类型的编译器和处理器重排序,JMM会针对编译器制定volatile重排序规则表: @@ -131,4 +131,4 @@ java编译器会在生成指令系列时在适当的位置会插入内存屏障 > 参考文献 -《java并发编程的艺术》 \ No newline at end of file +《java并发编程的艺术》