cos测试完毕,图床切回原版

This commit is contained in:
youthlql
2022-03-16 01:23:41 +08:00
parent 8c89dac3bf
commit 0660078ffa
47 changed files with 737 additions and 737 deletions

View File

@@ -291,7 +291,7 @@ d = e - f ;
由于处理器使用缓存和读写缓存冲区,这使得加载(load)和存储(store)操作看上去可能是在乱序执行,因为三级缓存的存在,导致内存与缓存的数据同步存在时间差。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0001.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0001.png">
@@ -343,7 +343,7 @@ int c = a + b;
冯诺依曼,提出计算机由五大组成部分,输入设备,输出设备存储器,控制器,运算器。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0002.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0002.png">
输入设备:鼠标,键盘等等
@@ -367,7 +367,7 @@ int c = a + b;
CPU的运算速度和内存的访问速度相差比较大。这就导致CPU每次操作内存都要耗费很多等待时间。内 存的读写速度成为了计算机运行的瓶颈。于是就有了在CPU和主内存之间增加缓存的设计。靠近CPU 的缓存称为L1然后依次是 L2L3和主内存CPU缓存模型如图下图所示。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0003.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0003.png">
CPU Cache分成了三个级别: L1 L2 L3。级别越小越接近CPU速度也更快同时也代表着容量越小。速度越快的价格越贵。
@@ -377,7 +377,7 @@ CPU Cache分成了三个级别: L1 L2 L3。级别越小越接近CPU
3、L3 Cache是三级缓存中大的一级例如12MB同时也是缓存中慢的一级在同一个CPU插槽 之间的核共享一个L3 Cache。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0004.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0004.png">
上面的图中有一个Latency指标。比如Memory这个指标为59.4ns表示CPU在操作内存的时候有59.4ns的延迟一级缓存最快只有1.2ns。
@@ -409,7 +409,7 @@ Cache的出现是为了解决CPU直接访问内存效率低下问题的。
每一个线程有自己的工作内存,工作内存只存储该线程对共享变量的副本。线程对变量的所有的操 作(读,取)都必须在工作内存中完成,而不能直接读写主内存中的变量,不同线程之间也不能直接 访问对方工作内存中的变量。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0005.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0005.png">
Java的线程不能直接在主内存中操作共享变量。而是首先将主内存中的共享变量赋值到自己的工作内存中再进行操作操作完成之后刷回主内存。
@@ -424,7 +424,7 @@ Java内存模型是一套在多线程读写共享数据时对共享数据的
JMM内存模型与CPU硬件内存架构的关系
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0006.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0006.png">
工作内存可能对应CPU寄存器也可能对应CPU缓存也可能对应内存。
@@ -434,9 +434,9 @@ JMM内存模型与CPU硬件内存架构的关系
## 再谈可见性
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0007.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0007.png">
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0008.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0008.png">
1、图中所示是 个双核 CPU 系统架构 每个核有自己的控制器和运算器其中控制器包含一组寄存器和操作控制器运算器执行算术逻辅运算。每个核都有自己的1级缓存在有些架构里面还有1个所有 CPU 共享的2级缓存。 那么 Java 内存模型里面的工作内存,就对应这里的 Ll 或者 L2 存或者 CPU 寄存器。
@@ -452,7 +452,7 @@ JMM内存模型与CPU硬件内存架构的关系
为了保证数据交互时数据的正确性Java内存模型中定义了8种操作来完成这个交互过程这8种操作本身都是原子性的。虚拟机实现时必须保证下面 提及的每一种操作都是原子的、不可再分的。
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0009.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0009.png">
> (1)lock:作用于主内存的变量,它把一个变量标识为一条线程独占的状态。
>
@@ -478,7 +478,7 @@ JMM内存模型与CPU硬件内存架构的关系
如果没有synchronized那就是下面这样的
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0010.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0010.png">
@@ -589,7 +589,7 @@ volatile不保证原子性只保证可见性和禁止指令重排
## CPU术语介绍
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0011.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0011.png">
@@ -717,7 +717,7 @@ public class VolatileExample {
**1、下面是保守策略下volatile写插入内存屏障后生成的指令序列示意图**
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0012.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0012.png">
> 图中的StoreStore屏障可以保证在volatile写之前其前面的所有普通写操作已经对任 意处理器可见了。这是因为StoreStore屏障将保障上面所有的普通写在volatile写之前刷新到主内存。这里比较有意思的是volatile写后面的StoreLoad屏障。此屏障的作用是避免volatile写与 后面可能有的volatile读/写操作重排序。因为编译器常常无法准确判断在一个volatile写的后面 是否需要插入一个StoreLoad屏障比如一个volatile写之后方法立即return。为了保证能正确 实现volatile的内存语义JMM在采取了保守策略在每个volatile写的后面或者在每个volatile 读的前面插入一个StoreLoad屏障。从整体执行效率的角度考虑JMM最终选择了在每个volatile写的后面插入一个StoreLoad屏障。因为volatile写-读内存语义的常见使用模式是:一个 写线程写volatile变量多个读线程读同一个volatile变量。当读线程的数量大大超过写线程时 选择在volatile写之后插入StoreLoad屏障将带来可观的执行效率的提升。从这里可以看到JMM在实现上的一个特点首先确保正确性然后再去追求执行效率
@@ -725,7 +725,7 @@ public class VolatileExample {
**2、下面是在保守策略下volatile读插入内存屏障后生成的指令序列示意图**
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0013.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0013.png">
> 图中的LoadLoad屏障用来禁止处理器把上面的volatile读与下面的普通读重排序。 LoadStore屏障用来禁止处理器把上面的volatile读与下面的普通写重排序。 上述volatile写和volatile读的内存屏障插入策略非常保守。在实际执行时只要不改变volatile写-读的内存语义,编译器可以根据具体情况省略不必要的屏障。
@@ -752,7 +752,7 @@ class VolatileBarrierExample {
针对readAndWrite()方法,编译器在生成字节码时可以做如下的优化
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0014.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0014.png">
注意最后的StoreLoad屏障不能省略。因为第二个volatile写之后方法立即return。此时编译器可能无法准确断定后面是否会有volatile读或写为了安全起见编译器通常会在这里插入一个StoreLoad屏障。
@@ -766,7 +766,7 @@ class VolatileBarrierExample {
X86处理器仅会对写-读操作做重排序。X86不会对读-读、读-写和写-写操作 做重排序因此在X86处理器中会省略掉这3种操作类型对应的内存屏障。在X86中JMM仅需在volatile写后面插入一个StoreLoad屏障即可正确实现volatile写-读的内存语义。这意味着在X86处理器中volatile写的开销比volatile读的开销会大很多因为执行StoreLoad屏障开销会比较大
<img src="https://img.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0015.png">
<img src="https://npm.elemecdn.com/youthlql@1.0.8/Java_concurrency/Source_code/Second_stage/0015.png">