synchronized和volatile关键字

java内存模型

java内存模型规定,将所有的变量都放在主内存,当线程使用变量时,会把该变量复制到线程的工作内存,因此线程读写变量操作的是自己工作内存中的变量。
java内存模型

下图是一个双核cpu的系统架构,每个核都有自己的控制器、运算器、一级缓存,其中控制器包含一组寄存器和操作控制器,运算器负责进行算术逻辑运算。在这个架构中cpu还共享一个二级缓存。

其中java内存模型里的线程工作内存对应就是一级缓存、二级缓存、cpu寄存器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DEpNnjQu-1592028132913)(/images/pasted-8.png)]

共享对象可见性

例如上图,假设线程A和线程B同时处理一个共享变量,且线程使用不同cpu运行,缓存都为空。
线程 A 首先获取共享变量 X 的值,由于两级 Cache 都没有命中 ,所以加载主 内存 中 X 的值,假如为 0。然后把 X=O 的值缓存到两级缓存, 线程 A 修改 X 的值为 1, 然后将其写入两级 Cache, 并且刷新到主内存。 线程 A 操作完毕后,线程 A 所在的 CPU 的两级 Cache 内和主内存里面的 X 的值都是 l 。
线程 B 获取 X 的值,首先一级缓存没有命中,然后看二级缓存,二级缓存命中了 , 所以返回 X= 1 ; 到这里一切都是正常的, 因为这时候主 内存中也是 X=l 。然后线 程 B 修改 X 的值为 2, 并将其存放到线程 2 所在的一级 Cache 和共享二级 Cache 中, 最后更新主内存中 X 的值为 2 : 到这里一切都是好的。
线程 A 这次又需要修改 X 的值, 获取时一级缓存命中, 并且 X=l ,到这里问题就 出 现了,明明线程 B 已经把 X 的值修改为了 2,为何线程 A 获取的还是 l 呢? 这 就是共享变量的内存不可见问题, 也就是线程 B 写入的值对线程 A 不可见。(来自于《java并发编程之美》)

使用synchronized和volatile关键字可以解决共享对象的不可见性

synchronized关键字

synchronized关键字是java提供的一个原子性内置锁(排他锁),同一时间最多只能有一个线程持有相同对象的锁。在线程进入synchronizedd代码块会获得对象锁,其他线程运行到synchronized会被堵塞挂起。
拿到对象锁的线程会在以下情况释放对象锁:
1.正常执行完同步代码块。
2.抛出异常。
3.调用wait等休眠方法

synchronized的内存语义

进入synchronized代码块会把同步代码块用到的变量从运行线程的工作内存中清除。这样获取变量时就不会从工作内存中获得,而是去主内存中获得。
退出synchronized代码块会把对同步代码块修改的变量刷新到主内存里去。

volatile关键字

当一个变量被volatile关键字声明时,工作线程获取该变量时会直接从主内存中获得,修改变量值会直接把值刷新到主内存中去。

两者对比

同:synchronized和volatile都可以实现在多线程中共享对象的可见性。
异:volatile只保证共享对象的可见性,不保证操作的原子性。
synchronized是比较重量级的操作,他会引起线程上下文的切换并带来线程调度开销。

猜你喜欢

转载自blog.csdn.net/printf_scnaf/article/details/106731543