java内存模型值synchronized和volatile
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java内存模型值synchronized和volatile相关的知识,希望对你有一定的参考价值。
多线程对共享变量的访问。
第一, 必须是共享变量。
第二, 必须是多个线程共享一个变量
第三, 因为多个线程都有自己的工作内存,那么除了主内存有共享变量值的原本,每个工作内存都有自己的变量副本,
第四, 线程对共享变量的所有操作都在自己的工作内存中进行,不能直接从主内存中进行读写
第五, 线程之间无法直接访问各自工作内存中的变量,线程变量值的传递必须经过主内存来传递。
Java语言层面支持的可见性支持:
synchronized:同步(原子性),内存可见性,
特性:1线程解锁前,必须把共享变量的最新之刷新到主内存中去。
2线程加锁时,将清空工作内存中共享变量的值,从而需要从主内存中重新读取最新的值。
这2个特性保证了线程解锁前对共享变量的修改在下次加锁前对其他线程可见。
线程执行互斥代码的过程:
获得互斥锁
清空工作内存
从主内存中拷贝变量的最新副本到工作内存
执行代码
将更改后的共享变量的值刷新到主内存
释放互斥锁
重排序
代码书写的顺序和实际执行的顺序不同,指令重排序是编译器或处理器为了提高程序性能而坐的优化。主要是有些代码重排序后更加符合处理器执行的方式。可以提高cpu的利用效率。
1, 编译器优化的重排序(编译器优化)主要是单线程中在不影响代码逻辑的前提下进行固有的优化。
2, 指令级并行重排序(处理器优化),很多电脑都是多核处理器,那么这个优化可以让处理器同时执行多个指令
3, 内存系统的重排序(处理器的优化),对读写缓存的优化,
因此我们在加载和执行代码的时候不一定是按照固有逻辑执行的。
只有数据依赖关系才会禁止指令重排序。
as-if-serial
无论如何重排序,程序执行的结果和代码的顺序执行结果要是一致的(java编译器,运行时和处理器都会保证java程序在单线程下遵循as-if-serial语义)
举例说明:
If(read)
{
A=b*3;
}
重排序后结果
Int temp=b*3;
If(read)
{
Result=mid;
}
但是在多线程中指令重排序可能导致造成内存可见性问题。由这个例子可以知道重排序+可能导致数据的不安全性,在多线程中。
那么导致共享变量在程序间不可见的原因(不加锁):
线程的交叉执行。
重排序结合线程交叉执行。
共享变量更新的值没有在工作内存和主内存间及时更新数据
那么加了synchronized为什么会变得安全
1, 同步,加了锁,加了锁只能同时只有一个线程来执行。
2, 既然加了锁,那么一段时间内只有一个线程执行代码,那么重排序也就无所谓了。
3, Synchronized的特性。
Volatile关键字:
Volatile:通过加入内存屏障和禁止重排序优化来实现的。
对volatile变量执行写操作时,会在写操作后加入一条store屏障指令。就是强制把cpu缓存的刷新到主内存。
对volatile变量执行读操作时,会在读操作前加入一条load屏障指令。就是强制把cpu缓存的刷新获取到主内存的值。
通俗来说:volatile变量在每次被线程访问时,都强迫从主内存中读取该变量的值,当变量改变时又要强迫刷新到主内存中去。这样不同线程总能看到该变量最新的值。
不具有原子性,不能保证同一时间只有一个线程对这个变量的值来修改。
那么什么情况适合使用volatile关键字变量?
- 对变量的写入操作不依赖当前值。比如 不满足的情况:num++, num=num+1;
- 该变量没有包含在具有其他变量的不变式中。
Volatile更轻量级。
还有java1.5以后引用的ReentrantLock()对象来加锁。
private Lock lock=new ReentrantLock();
lock.lock();
{
代码:一般是try{}finally{}
}
lock.unlock();
以上是关于java内存模型值synchronized和volatile的主要内容,如果未能解决你的问题,请参考以下文章