JUC-共享模型之内存

1.Java内存模型

JMM即Java Memory Model,它定义了主存、工作内存抽象概念,底层对应着CPU寄存器、缓存、硬件内存、CPU指令优化等。
JMM体现在以下几个方面

  • 原子性-保证指令不会受到线程上下文切换的影响
  • 可见性-保证指令不会受cpu缓存的影响
  • 有序性-保证指令不会受cpu指令并行优化的影响

退不出的循环
先来看一个现象,main线程对run变量的修改对于t线程不可见,导致了t线程无法停止:

static boolean run = true;
public static void main(String[] args) throws InterruptedException {
	Thread t = new Thread(() -> {
		while(run){
			// ....
		}
	});
	t.start();
	sleep(1);
	log.debug("停止t");
	run = false; // 线程t不会如预想的停下来
}

为什么呢?分析一下:
1.初始状态,t线程刚开始从主内存读取了run的值到工作内存。
在这里插入图片描述
2.因为t线程要频繁从主内存中读取run的值,JIT编译器会将run的值缓存到自己工作内存中的高速缓存区中,减少对主存中run的访问,提高效率
在这里插入图片描述
3.休眠了1s以后,main线程修改了run的值,并同步到主存,但t是从自己工作内存中的高速缓存中读取这个变量的值,结果永远是旧值
在这里插入图片描述
解决方法
volatile(易变关键字)
它可以用来修饰成员变量和静态变量,它可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的值,线程操作volatile变量都是直接操作主存

volatile static boolean run = true;
public static void main(String[] args) throws InterruptedException {
	Thread t = new Thread(() -> {
		while(run){
			// ....
		}
	});
	t.start();
	sleep(1);
	log.debug("停止t");
	run = false; 
}

也可以用synchronized,但是使用synchronized,要创建Monitor,属于比较重量级的操作,打包volatile相对轻量,在解决可见性层面推荐使用volatile

static boolean run = true;
final static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
	Thread t = new Thread(() -> {
		while(true){
			synchronized(lock){
				if(!run){
					break;
				}
			}
		}
	});
	t.start();
	sleep(1);
	log.debug("停止t");
	synchronized(lock){
		run = false; 
	}
}

2.volatile原理

2.1.如何保证可见性

写屏障(sfence)保证在该屏障之前的,对共享变量的改动,都同步到主存当中

public void actor2(I_Result r){
	num = 2;
	ready = true; // ready是volatile赋值带写屏障
	//写屏障
}

而读屏障(lfence)保证在该屏障之后,对共享变量的读取,加载的是主存中最新数据

public void actor1(I_Result r){
	//读屏障
	// ready是volatile 读取值带读屏障
	if(ready){
		r.r1 = num + num;
	}else {
		r.r1 = 1;
	}
}

在这里插入图片描述

2.2.如何保证有序性

写屏障会确保指令重排时,不会将写屏障之前的代码排在写屏障之后

public void actor2(I_Result r){
	num = 2;
	ready = true; 
}

读屏障会确保指令重排序时,不会将读屏障之后的代码排在读屏障之前

public void actor1(I_Result r){
	//读屏障
	// ready是volatile 读取值带读屏障
	if(ready){
		r.r1 = num + num;
	}else {
		r.r1 = 1;
	}
}

在这里插入图片描述
但是还是那句话,不能解决指令交错:

  • 写屏障仅仅是保证之后的读能够读到最新的结果,但不能保证读跑到它前面去
  • 而有序性的保证也只是保证了本线程内相关代码不被重排序
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值