JMM的内存可见性保证
按程序类型,Java程序的内存可见性保证可以分为下列3类:
单线程程序。单线程程序不会出现内存可见性问题。编译器、runtime和处理器会共同确保单线
程程序的执行结果与该程序在顺序一致性模型中的执行结果相同。
正确同步的多线程程序。正确同步的多线程程序的执行将具有顺序一致性(程序的执行结果与该
程序在顺序一致性内存模型中的执行结果相同)。这是JMM关注的重点,JMM通过限制编译器和处
理器的重排序来为程序员提供内存可见性保证。
未同步/未正确同步的多线程程序。JMM为它们提供了最小安全性保障:线程执行时读取到的
值,要么是之前某个线程写入的值,要么是默认值。未同步程序在JMM中执行时,整体上是无序
的,其执行结果无法预知。 JMM不保证未同步程序的执行结果与该程序在顺序一致性模型中的执行
结果一致。
未同步程序在JMM中的执行时,整体上是无序的,其执行结果无法预知。未同步程序在两个模型中的
执行特性有如下几个差异。
1)顺序一致性模型保证单线程内的操作会按程序的顺序执行,而JMM不保证单线程内的操作会按程序
的顺序执行,比如正确同步的多线程程序在临界区内的重排序。
2)顺序一致性模型保证所有线程只能看到一致的操作执行顺序,而JMM不保证所有线程能看到一致的
操作执行顺序。
3)顺序一致性模型保证对所有的内存读/写操作都具有原子性,而JMM不保证对64位的long型和
链接:download
提取码:wit3
失效+\/❤:cowcow2100
package com.tuling.jucdemo.jmm;
import java.util.concurrent.locks.LockSupport;
import com.tuling.jucdemo.factory.UnsafeFactory;
/**
* @author Fox
*
* -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp
* hsdis-amd64.dll
* 可见性案例
*/
public class VisibilityTest {
// storeLoad JVM内存屏障 ----> (汇编层面指令) lock; addl $0,0(%%rsp)
// lock前缀指令不是内存屏障的指令,但是有内存屏障的效果 缓存失效
private volatile boolean flag = true;
private Integer count = 0;
public void refresh() {
flag = false;
System.out.println(Thread.currentThread().getName() + "修改flag:"+flag);
}
public void load() {
System.out.println(Thread.currentThread().getName() + "开始执行.....");
while (flag) {
//TODO 业务逻辑
count++;
//JMM模型 内存模型: 线程间通信有关 共享内存模型
//没有跳出循环 可见性的问题
//能够跳出循环 内存屏障
//UnsafeFactory.getUnsafe().storeFence();
//能够跳出循环 ? 释放时间片,上下文切换 加载上下文:flag=true
//Thread.yield();
//能够跳出循环 内存屏障
//System.out.println(count);
//LockSupport.unpark(Thread.currentThread());
//shortWait(1000000); //1ms
//shortWait(1000);
// try {
// Thread.sleep(1); //内存屏障
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//总结: Java中可见性如何保证? 方式归类有两种:
//1. jvm层面 storeLoad内存屏障 ===> x86 lock替代了mfence
// 2. 上下文切换 Thread.yield();
}
System.out.println(Thread.currentThread().getName() + "跳出循环: count=" + count);
}
public static void main(String[] args) throws InterruptedException {
VisibilityTest test = new VisibilityTest();
// 线程threadA模拟数据加载场景
Thread threadA = new Thread(() -> test.load(), "threadA");
threadA.start();
// 让threadA执行一会儿
Thread.sleep(1000);
// 线程threadB通过flag控制threadA的执行时间
Thread threadB = new Thread(() -> test.refresh(), "threadB");
threadB.start();
}
public static void shortWait(long interval) {
long start = System.nanoTime();
long end;
do {
end = System.nanoTime();
} while (start + interval >= end);
}
}
部分目录
并发编程专题
【回放】并发编程之深入理解JMM&并发三大特性(一)(10月27日 20:10-22:00)
【回放】并发编程之深入理解JMM&并发三大特性(二)(10月29日 20:10-22:00)
【回放】并发List、Set、ConcurrentHashMap底层原理(10月31日 20:10-22:00)
【回放】Java并发线程池底层原理详解与源码分析(11月3日 20:10-22:00)
【回放】并发编程之深入理解Java线程(11月5日 20:10-22:00)
【回放】并发编程之CAS&Atomic原子操作详解(11月7日 20:10-22:00)
【回放】并发锁机制之深入理解synchronized(一)(11月10日 20:10-22:00)
06
并发编程专题
【回放】并发锁机制之深入理解synchronized(二)(11月12日 20:10-22:00)
【回放】深入理解AQS之独占锁ReentrantLock源码分析(11月14日 20:10-22:00)
【回放】Semaphorer&CountDownLatch详解(11月17日 20:10-22:00)
【回放】深入理解AQS之CyclicBarrie详解(11月19日 20:10-22:00)
【回放】深入理解AQS之ReentrantReadWriteLock实战(11月21日 20:10-22:00)
【回放】阻塞队列BlockingQueue实战及其原理分析一(11月24日 20:10-22:00)
【回放】阻塞队列BlockingQueue实战及其原理分析二(11月26日 20:10-22:00)
【回放】并发编程之ForkJoin工作原理分析(11月28日 20:10-22:00)
【回放】CompletableFuture实战&Disruptor原理剖析(12月1日 20:10-22:00)
【回放】常用并发设计模式精讲(12月3日 20:10-22:00)
07
性能调优/MySQL
【录播】深入理解Mysql索引底层数据结构与算法(126分钟)
【录播】Explain详解与索引最佳实践(142分钟)
【录播】SQL底层执行原理详解(118分钟)
【录播】Mysql索引优化实战一(150分钟)
【录播】Mysql索引优化实战二(130分钟)
【录播】深入理解Mysql事务隔离级别与锁机制(149分钟)
【录播】深入理解MVCC与BufferPool缓存机制(132分钟)
【回放】高性能业务表结构设计和索引知识深化(12月12日 20:10-22:00)