参考:https://blog.youkuaiyun.com/weixin_73077810/article/details/132804522
内存屏障主要用于解决线程的可见性、有序性问题


代码案例:



ReentrantLock保证可见性的原理
在 lock.lock() 和 lock.unlock() 时,都会操作 AbstractQueuedSynchronizer类 中的一个变量 state,这个变量是 volatile 修饰的,volatile变量的语句对应的汇编码指令中会多加一行lock addl $0x0, (%esp),这一行的作用是:
(1)将工作内存修改了的缓存(不仅仅是该变量的缓存)都强制刷新回主内存
(2)把其他CPU对应缓存行标记为invalid状态,那么在读取这一部分缓存时,必须回主内存读取。这样也就保证了线程间的可见性
具体来说,当一个线程获取ReentrantLock锁时(state值改变),它会将主内存中的数据刷新到自己的工作内存中(state值改变之后的所有对共享变量的读操作)。而当一个线程释放ReentrantLock锁时(state值改变),它会将自己工作内存中的数据刷新到主内存中(state值改变之前的所有对共享变量的写操作),这样其他线程就能够读取到最新的值。
备注:
volatile变量改变之后所有的共享变量读操作都会从主内存中重新获取最新值到工作内存,volatile变量改变之前的所有共享变量的改变都会刷新到主内存
volatile读写内代码可以重排序,和synchronized同步内的代码一样都可以重排序
一个自己写的测试类
import org.junit.jupiter.api.Test;
import java.util.concurrent.TimeUnit;
class TestCache {
private volatile Boolean r=false;
private String string="1";
Thread readThread=null;
Thread writeThread=null;
/**
*
*/
@Test
void contextLoads() throws InterruptedException {
readThread=new Thread(()->{
while (true){
//下面的这2个if顺序说明了:volatile后的共享变量重新获取了,之前的不再重新获取
// if( r && string.contentEquals("2") ){//能看到string的变化
if( string.contentEquals("2") && r ){//不能看到string的变化
System.out.println("end task");
break;
}
}
});
writeThread=new Thread(()->{
string="2";
r=true;
});
readThread.start();
TimeUnit.SECONDS.sleep(2);
writeThread.start();
readThread.join();
}
}
volatile用在一个对象上时,禁止对象的创建重排序,还有对象的引用发生变化另一个线程立即可见。
synchronized和volatile类似的功能
synchronized可见性:或Lock接口进行同步时,获取锁时,会从主内存读取最新数据;在释放锁之前,会将修改的数据刷新到主内存中。
synchronized重排序:synchronized内的代码禁止溢出{},但同步内的代码可以重排序
在一个方法内如果有2个方法,这2个方法会被重排序吗?
编译器通常不会重排序这两个方法调用,因为它们是独立的语句,并且依赖于方法的调用顺序。编译器优化主要关注于单个方法内部的操作,而不是跨方法的调用顺序。
本文详细解释了内存屏障如何解决线程可见性和有序性问题,以及ReentrantLock利用volatile变量保证锁操作后数据同步的机制。通过测试类展示了volatile变量如何影响共享变量的更新和读取。
1274

被折叠的 条评论
为什么被折叠?



