@chenxuxiong
2016-05-27T06:51:19.000000Z
字数 1988
阅读 600
JAVA基础
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。
另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。
2)禁止进行指令重排序。
volatile关键字禁止指令重排序有两层意思:
1)当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
2)在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。
可能上面说的比较绕,举个简单的例子:
//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是可见的。
我们可以观察对比加入volatile和未加入volatile关键字是所生成的汇编代码的差别。
通过对比我们发现有volatile修饰的变量赋值后,多执行了一个“lock add $0x0”的操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置)。只有一个cpu访问内存时,不需要内存屏障;但若有多个,则需要内存屏障了,达到“指令重排序无法越过内存屏障”
用volatile必须具备以下2个条件:
1)对变量的写操作不依赖于当前值,或者能够确保只有单一的线程修改变量的值。
2)该变量没有包含在具有其他变量的不变式中
例如 boolean类型,单例模式的双重检验锁
参考:http://www.cnblogs.com/dolphin0520/p/3920373.html
当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
synchronized能保证操作的原子性
1.可以锁住对象,synchronized(this),锁住对象,只针对同个对象
2.可以锁住类,syschronized(XXX.class),锁住类,针对所有对象
3.可以锁住方法,syschronized int increa().锁住方法之前,等同锁住类
4.要注意的是,锁住的时候,其他同步代码块的部分会被暂时阻塞,非同步代码块不会阻塞,可以被访问
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
二、但是,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
五、以上规则对其它对象锁同样适用.
参考:http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html