volatile与Synchronized
摘自: https://blog.csdn.net/zxh476771756/article/details/78685581
一、JVM内存模型:
JVM将内存组织为主内存和工作内存两个部分。
主内存主要包括本地方法区和堆。
每个线程都有一个工作内存,工作内存中主要包括两个部分,一个是属于该线程私有的栈和对主存部分变量拷贝的寄存器(包括程序计数器PC和cup工作的高速缓存区)。
1.所有的变量都存储在主内存中(虚拟机内存的一部分),对于所有线程都是共享的。
2.每个线程都有自己的工作内存,工作内存中保存的是该线程使用到的变量副本(该副本就是主内存中该变量的一份拷贝),线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。
3.线程之间无法直接访问对方的工作内存中的变量,线程间变量的传递均需要通过主内存来完成。
Java内存模型(JMM)规定了jvm有主内存,主内存是多个线程共享的。当new一个对象的时候,也是被分配在主内存中,每个线程都有自己的工作内存,工作内存存储了主存的某些对象的副本。
所以可能会存在这种情况:当线程2更改了xxx变量的值之后,但是还没来得及写入主存当中,线程2转去做其他事情了,那么线程1由于不知道线程2已经对xxx变量的更改,因此还会一直循环下去。
java中volatile解决了可见性问题。
二、volatile关键字
volatile就是表示某人或某物是不稳定的、易变的。
volatile作为java中的关键词之一,用以声明变量的值可能随时会别的线程修改,使用volatile修饰的变量会强制将修改的值立即写入主存,主存中值的更新会使缓存中的值失效(非volatile变量不具备这样的特性,非volatile变量的值会被缓存,线程A更新了这个值,线程B读取这个变量的值时可能读到的并不是是线程A更新后的值)。volatile会禁止指令重排。
1、volatile保证内存可见性。
可见性是指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。
普通变量与volatile变量的区别是:
volatile的特殊规则保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。
2、能禁止指令重排序,所以volatile能在一定程度上保证有序性。
1)当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
2)在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。
例1:
//x、y为非volatile变量
//flag为volatile变量 x = 2; //语句1
y = 0; //语句2
flag = true; //语句3
x = 4; //语句4
y = -1; //语句5
由于flag变量为volatile变量,那么在进行指令重排序的过程的时候,不会将语句3放到语句1、语句2前面,也不会讲语句3放到语句4、语句5后面。但是要注意语句1和语句2的顺序、语句4和语句5的顺序是不作任何保证的。
并且volatile关键字能保证,执行到语句3时,语句1和语句2必定是执行完毕了的,且语句1和语句2的执行结果对语句3、语句4、语句5是可见的。
例2:
//线程1:
context = loadContext(); //语句1
inited = true; //语句2 //线程2:
while(!inited ){
sleep()
}
doSomethingwithconfig(context);
而有可能语句2会在语句1之前执行,那么久可能导致context还没被初始化,而线程2中就使用未初始化的context去进行操作,导致程序出错。
这里如果用volatile关键字对inited变量进行修饰,就不会出现这种问题了,因为当执行到语句2时,必定能保证context已经初始化完毕。
3、不能保证原子性,volatile无法保证对变量的任何操作都是原子性的,比如i++。
三、synchronized关键字
synchronized:关键词,它依赖于JVM,保证了同一时刻只能有一个线程在作用对象的作用范围内进行操作。
1、内存可见性:
同步块的可见性是由“对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store、write操作)”这条规则获得的。
在Java内存模型中,synchronized规定,线程在加锁时,先清空工作内存→在主内存中拷贝最新变量的副本到工作内存→执行完代码→将更改后的共享变量的值刷新到主内存中→释放互斥锁。
2、操作的原子性:
原子性提供了程序的互斥操作,同一时刻只能有一个线程能对某块代码进行操作。
synchronized
修饰的代码块,作用于调用的对象synchronized
修饰的方法,作用于调用的对象synchronized
修饰的静态方法,作用于这个类的所有对象synchronized
修饰的类,作用于这个类的所有对象
3、有序性
java用synchronized关键字做为多线程并发环境的执行有序性的保证手段之一。当一段代码会修改共享变量,这一段代码成为互斥区或临界区,为了保证共享变量的正确性,synchronized标示了临界区。
synchronized(锁){
临界区代码
}
一个线程执行临界区代码过程如下:
1 获得同步锁
2 清空工作内存
3 从主存拷贝变量副本到工作内存
4 对这些变量计算
5 将变量从工作内存写回到主存
6 释放锁
可见,synchronized既保证了多线程的并发有序性,又保证了多线程的内存可见性。
四、Synchronized和volatile的比较
1)Synchronized保证内存可见性和操作的原子性,Volatile只能保证内存可见性。
2)volatile不需要加锁,比Synchronized更轻量级,并不会阻塞线程(volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。)
4)volatile标记的变量不会被编译器优化,而synchronized标记的变量可以被编译器优化(如编译器重排序的优化).
5)volatile是变量修饰符,仅能用于变量,而synchronized是一个方法或块的修饰符。
CAS (Compare-and-Swap) 比较并替换--处理器指令
CAS指令需要有3个操作数,分别是内存位置(在Java中可以简单理解为变量的内存地址,用V表示)、旧的预期值(用A表示)和新值(用B表示)。CAS指令执行时,当且仅当V符合旧预期值A时,处理器用新值B更新V的值,否则它就不执行更新,但是无论是否更新了V的值,都会返回V的旧值,上述的处理过程是一个原子操作。
在jdk1.5之后,Java程序中才可以使用CAS操作,该操作由sun.misc.Unsafe类里面的compareAndSwapInt和compareAndSwapLong等几个方法包装提供,虚拟机在内部对这些方法做了特殊处理,即时编译出来的结果就是一条平台相关的处理器CAS指令,没有方法调用的过程,或者可以认为是无条件关联进去了。
由于Unsafe类不是提供给用户程序调用的类(Unsafe.getUnsafe()的代码中限制了只有启动类加载器(Bootstrap ClassLoader)加载的Class才能访问它),因此,如果不采用反射手段,我们只能通过其他的Java API来间接使用它,如java.util.concurrent包里面的整数原子类AtomicInteger,其中的compareAndSet()和getAndIncrement()等方法都使用了Unsafe类的CAS操作。
Jdk1.6,java.util.concurrent.atomic包中,AtomicInteger类有个方法:
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
最新文章
- DRY(Don't Repeat Yourself )原则
- 使用腾讯开发平台获取QQ用户数据资料
- [vijos1982][NOIP2015]子串
- Error Handling
- sublime exclude folder?
- iOS 常用第三方
- Delphi 数据类型列表 good
- Java中finally你知多少
- java算法 蓝桥杯 乘法运算
- flask入门篇
- Tomcat7 JDK8 Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x0000000540000000, 5368709120, 0) failed; error='Cannot allocate memory' (errno=12)
- 关于Python, ftplib模块中的cwd()进入含中文目录失败的问题
- libnids-1.24 使用源码问题
- RFS实例登录126邮箱/利用cookie登陆百度
- lnmp+zabbix 3.2 的编译安装
- BZOJ4415: [Shoi2013]发牌 树状数组+二分
- Tensoflw.js - 02 - 模型与内存管理(易懂)
- Spring框架之Bean的作用范围和生命周期的注解
- Hdu1547 Bubble Shooter 2017-01-20 18:38 44人阅读 评论(0) 收藏
- Windows远程桌面连接CentOS 7