volatile关键字与Java同步内存模型

       java中的volatile关键字,使用并不多,它的主要作用是使变量在线程间可见。

       假设有两个线程t1和t2,它们使用一个共享变量:int a=0; 。t1对a进行了赋值:a=10。但这时在t2线程中读取a变量仍然是0,也就是说t1对a的修改对t2不可见,反之也是一样的。这是因为在Java内存模型(JMM)中,每个线程分配了独立的内存空间,其内存空间中特别有一块独立的运行空间,线程运行时会从主内存中将当前线程所使用到的共享变量拷贝一个副本放入这块运行空间,线程运行时直接在自己的运行空间中读写变量数据。即在t1和t2的内存空间中有两份相互独立的a变量,它们之间数据不同步。这样做的目的当然是为了提高线程的效率。

       但通常情况下,既然a变量是共享的,我们当然希望当t1中对a进行了修改,能够立即通知t2,使t2读出a=10。

        这种问题当然是可以解决的,最常用的就是通过 synchronized 关键字来实现同步。同步方法或同步代码块执行时,会强制进行变量的同步。但使用这种方式必须加锁,同一时刻只能有一个线程进来操作变量,效率较低,性能不好。

        volatile 变量是另一种选择。Java™ 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量。这两种机制的提出都是为了实现代码线程的安全性。其中 Volatile 变量的同步性较差(但有时它更简单并且开销更低),而且其使用也更容易出错。Java 语言中的 volatile 变量可以被看作是一种 “程度较轻的 synchronized”;与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分。volatile 变量具有 synchronized 的可见性和有序性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。以开头的例子,变量a如果被volatile关键字修饰了,当t1将变量a改变时,则会立即将a写回主内存,线程执行引擎会强制其它使用a变量的线程去主内存中重新读取a变量。此外,volatile可以禁止指令重排优化,从而保证有序性。

       volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。因此,单独使用 volatile 还不足以实现计数器、互斥锁或任何具有与多个变量相关的不变式(Invariants)的类(例如 “start <=end”)。您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:
       1.对变量的写操作不依赖于当前值。
       2.该变量没有包含在具有其他变量的不变式中。

       实际上,这些条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。 第一个条件的限制使 volatile 变量不能用作线程安全计数器。虽然增量操作(x++)看上去类似一个单独操作,实际上它是一个由读取-修改-写入操作序列组成的组合操作,必须以原子方式执行,而 volatile 不能提供必须的原子特性。实现正确的操作需要使 x 的值在操作期间保持不变,而 volatile 变量无法实现这点。

       使用 volatile 变量的主要原因是其简易性:在某些情形下,使用 volatile 变量要比使用相应的锁简单得多。使用 volatile 变量次要原因是其性能:某些情况下,volatile 变量同步机制的性能要优于锁。

      volatile的英文意思是“易变的、不稳定的”,这也正是volatile关键字的语义。转眼间2016年已到了最后一个月,转眼又是一年,踌躇辗转后归于平淡,世上事可遇而不可求。但做好事,莫问前程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值