jvm-内存可见性

432 阅读3分钟

是什么

对象在内存里的可见性

本质

线程自己的空间,线程共有的空间。

内存划分

1.对象内存
线程共享的数据,堆对象数据

2.线程内存
属于每个线程的数据,栈数据

内存模型

内存模型(是规范)和内存划分(是规范的具体实现)可以划等号!虽然很多介绍说不是同一个层次的划分,但是这样很方便理解。


内存模型规范了三大特性
1.原子性
指的是给代码块上锁

2.内存可见性
读线程立即看到写线程的最新数据,而不是旧数据/失效数据。

3.有序性
确保线程按操作顺序来执行指令


内存可见性volatile 和内存模型之间的关系?
1.内存模型架构图
线程X-线程X内存-主内存

2.如何确保读到最新数据?
不同线程必须通过主内存交换数据,使用volatile可以确保每次读数据之前,先从主内存复制数据到线程内存,这样即可确保读线程可以读到最新数据!

3.volatile 会禁止CPU指令重排!
synchronized确保同一时刻只有一个线程执行代码块!

blog.csdn.net/qq_34964197…

synchronized可以满足内存模型的三大特性

优点
满足内存模型三大特性

缺点
锁的粒度太大,导致并发性能低


最佳实践
1.其实,synchronized已经够用了,因为jdk后期版本已经对它做了优化。
2.除非,实在程序员需要控制锁的其他特性。

--- 内存可见性 ---

到底什么是内存可见性

1.首先,我们同步synchronized的时候,是保护了一块代码块。这是其一。 其二,代码块的数据/对象,只能被当前线程修改。

2.如何,读呢?这个对象还允许被其他线程读吗,在同步线程没有执行完成同步代码块的时候?这里的问题,就是对象在内存里的可见性的问题。

3.我们当然想要别的线程可以立马看到同步代码块里的对象数据被改变。


问题是难道不是一个线程修改了数据,其他线程立马可以看到改变的数据吗?
答案是不一定。


为什么看不到数据呢?
因为会发生指令重排序。
即读线程先执行,写线程后执行,所以就导致读线程看不到被写线程改变的数据。


那怎么解决这个问题呢?
方法1
get方法也同步。

方法2 对数据进行同步,即使用volatile同步数据。

总结
不管是用哪一种方法,唯一的目的就是读共享数据的线程也必须要上锁。这样读到的数据,才是最新值,而不是失效值。


加锁的目的
有两个,
1.不仅仅同步写
2.而且,应该同步读,否则读线程可能读到失效值/旧数据

谁来重排序指令?

cpu。

编译器也可以。

volatile

只能确保对象的内存可见性,不能确保原子性——因为它没有锁。


总结
虽然,volatile的作用,可以理解为给写读数据的set/get方法上锁,但是并不能确保原子性,所以还是等于没有上锁。
那么,之所以这么理解,只能为了方便理解。
由此可见,这真不是一个好的设计,描述又描述不清楚,用的人程序员也是一头雾水,实践当中极少使用。
所以,基本上可以忽略不计,除了面试的时候,偶尔会提一下。

最佳实践

如果要解决内存的可见性问题,首选给读线程加锁,而不是给数据加volatile。