多线程-内存可见性

由于java内存模型定义了对底层数据访问的规则,线程读取写入数据都是在工作内存中进行,并在某些时候(不确定)将值刷新到主内存中。所以导致在多个线程的工作内存中的共享变量的副本值是不相同的。如果一个线程修改了共享变量值,由于没有及时将值刷新到主内存中,其他线程也无法及时获取最新值,所以出现可见性问题。

1.如何保证线程安全性?
2.共享变量可见性实现原理?支持方式?
3.synchronized实现可见性原理?
4.volatile实现可见性原理
5.synchronized和volatile对比


1.如何保证线程安全性?

出现线程不安全的原因是由于,多线程对共享可变变量的操作,控制对状态变量的访问确保其一致性。可从以下三方面入手:

  • 确保对共享变量的复合操作在执行过程中不会被其他线程打断。如i++操作。即操作原子性。
  • 确保对共享变量的修改能够及时被其他线程发现。即可见性
  • 计算机会对指令进行重排序优化,在多线程中对指令进行重排序优化,可能不会出现预期结果,如何控制有序性。

本文主要针对可见性涉及到的俩个关键字进行分析。

2.共享变量可见性实现原理,支持可见性方式?

原理:由于java内存模型定义的原因,线程操作数据是在其工作内存进行,导致不同线程的工作内存副本不一致情况。所以针对这个原有,如果能够确保一个线程修改数据后,另一个线程能够及时获取最新值,可解决可见性问题。
主要涉及:

  1. 线程修改后的变量能够及时更新到主内存中
  2. 其他线程能及时将共享变量的最新值从主内存中刷新到工作内存中

支持可见性方式:

synchronized:加锁
volatile
共享可变变量转换为私有变量—–栈封闭
共享变量转换为共享不可变变量

3.synchronized关键字实现可见性原理

说明:synchronized关键字对应于java内存模型中定义的lock和unlock俩个底层操作。对于使用synchronized关键字的方法或块,必须要获得对应的锁,才能继续操作,从而保证同一时刻只有一个线程进入对应的方法,对共享可变变量进行操作。

实现可见性原理:

  1. 线程解锁前unlock,将共享变量的最新值刷新到主内存中
  2. 线程加锁时lock,将清空工作内存共享变量的值,从而使线程需要从主内存中获取共享变量最新值将其刷新到工作内存中

synchronized执行过程:

  1. 获取互斥锁
  2. 清空工作内存
  3. 从主内存拷贝变量的最新副本到工作内存
  4. 执行代码
  5. 将更改后的共享变量值刷新到主内存
  6. 释放互斥锁

4.volatile实现原理

说明:volatile通过内存屏障和禁止指令重排序确保可见性

原理:

  • 对volatile变量进行写操作时,会在写操作后加一条store内存屏障指令。强制将变量刷新到主内存。
  • 对volatile变量进行读操作时,会在读操作前加一条load内存屏障指令。确保在每次读取前先从主内存中刷新最新值到工作内存中。

5.synchronized和volatile比较

  • volatile是轻量级同步机制,不需要加解锁操作。通过添加内存屏障指令来确保可见性
  • synchronized能够支持原子性,volatile无法保证原子性
  • synchronized可修饰方法(实例方法、静态方法)、块,volatile只能修饰变量。

总结:

多线程中存在可见性问题是因为多线程对同一对象进行操作,并且该对象中存在共享变量。解决可见性问题,就是要控制对共享可变变量的访问。这里提到的有synchronized、volatile、final以及将共享变量转换为方法变量。

猜你喜欢

转载自blog.csdn.net/demodan/article/details/80583290