只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:1对变量的写操作不依赖于当前值。 2该变量没有包含在具有其他

转载 2015年07月25日 09:05:45


Java™ 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量。这两种机制的提出都是为了实现代码线程的安全性。其中 Volatile 变量的同步性较差(但有时它更简单并且开销更低),而且其使用也更容易出错。在这期的 Java 理论与实践 中,Brian Goetz 将介绍几种正确使用 volatile 变量的模式,并针对其适用性限制提出一些建议。 
Java 语言中的 volatile 变量可以被看作是一种 “程度较轻的 synchronized”;与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分。本文介绍了几种有效使用 volatile 变量的模式,并强调了几种不适合使用 volatile 变量的情形。 

锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility)。互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议,这样,一次就只有一个线程能够使用该共享数据。可见性要更加复杂一些,它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的 —— 如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前的值或不一致的值,这将引发许多严重问题。 

Volatile 变量 

Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。Volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。因此,单独使用 volatile 还不足以实现计数器、互斥锁或任何具有与多个变量相关的不变式(Invariants)的类(例如 “start <=end”)。 

出于简易性或可伸缩性的考虑,您可能倾向于使用 volatile 变量而不是锁。当使用 volatile 变量而非锁时,某些习惯用法(idiom)更加易于编码和阅读。此外,volatile 变量不会像锁那样造成线程阻塞,因此也很少造成可伸缩性问题。在某些情况下,如果读操作远远大于写操作,volatile 变量还可以提供优于锁的性能优势。 

正确使用 volatile 变量的条件 

您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件: 

1对变量的写操作不依赖于当前值。 
2该变量没有包含在具有其他变量的不变式中。 

实际上,这些条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。 

第一个条件的限制使 volatile 变量不能用作线程安全计数器。虽然增量操作(x++)看上去类似一个单独操作,实际上它是一个由读取-修改-写入操作序列组成的组合操作,必须以原子方式执行,而 volatile 不能提供必须的原子特性。实现正确的操作需要使 x 的值在操作期间保持不变,而 volatile 变量无法实现这点。(然而,如果将值调整为只从单个线程写入,那么可以忽略第一个条件。) 

大多数编程情形都会与这两个条件的其中之一冲突,使得 volatile 变量不能像 synchronized 那样普遍适用于实现线程安全。清单 1 显示了一个非线程安全的数值范围类。它包含了一个不变式 —— 下界总是小于或等于上界。

。。。
package linyumin.test.thread;
/**
 * 
 * @author llade
 *
 */
public class VolatileObjectTest {


/**
* 相信绝大多数使用JAVA的人都没试出volatile变量的区别。献给那些一直想知道volatile是如何工作的而又试验不出区别的人。
* 成员变量boolValue使用volatile和不使用volatile会有明显区别的。
* 本程序需要多试几次,就能知道两者之间的区别的。
* @param args
*/
public static void main(String[] args) {
final VolatileObjectTest volObj=new VolatileObjectTest();
Thread t2=new Thread(){
public void run(){
System.out.println("t1 start");
for(;;){
volObj.waitToExit();
}
}
};
t2.start();
Thread t1=new Thread(){
public void run(){
System.out.println("t2 start");
for(;;){
volObj.swap();
}
}
};
t1.start();
}


boolean boolValue;//加上volatile 修饰的是时候,程序会很快退出,因为volatile 保证各个线程工作内存的变量值和主存一致。所以boolValue == !boolValue就成为了可能。


public void waitToExit() {
if(boolValue == !boolValue)System.exit(0);//非原子操作,理论上应该很快会被打断。实际不是,因为此时的boolValue在线程自己内部的工作内存的拷贝,因为它不会强制和主存区域同步,线程2修改了boolValue很少有机会传递到线程一的工作内存中。所以照成了假的“原子现象”。
}

public void swap() {//不断反复修改boolValue,以期打断线程1.
boolValue = !boolValue;
}


}


相关文章推荐

多线程安全(-)之正确使用_volatile_变量

多线程安全(-)之正确使用 Volatile 变量volatile 变量使用指南本篇文章转载自:Java 理论与实践: 正确使用 Volatile 变量Java™ 语言包含两种内在的同步机制:同步块(...

Linux下面的线程锁,条件变量以及信号量的使用

From  http://www.cppblog.com/converse/archive/2009/01/15/72064.html 一) 线程锁 1) 只能用于"锁"住临界代码区域 2) 一...

1)算术运算;2)指针地址操作;3)位运算 不使用第三方变量交换两个变量的值

不使用第三方变量交换两个变量的值(c#)2009-07-22 16:47这需要进行位操作,必较麻烦的, 在学习程序语言和进行程序设计的时候,交换两个变量的值是经常要使用的。通常我们的做法是(尤其...

volatile变量详解

  • 2013年12月31日 17:45
  • 202KB
  • 下载

volatile变量

  • 2009年06月05日 15:17
  • 85KB
  • 下载

从头认识多线程-3.2 使用volatile声明的变量的写操作是非原子性的

这一章节我们来讨论一下使用volatile声明的变量的各种操作是非原子性的。1.上一章节我们已经提到,volatile把工作内存里面变量的改变同步到主内存, 使得各个线程能够把该变量当成是整体的状态控...

利用条件变量实现线程安全队列

原文: Implementing a Thread-Safe Queue using Condition Variables  利用条件变量实现线程安全队列 One problem that...
  • ykxggg
  • ykxggg
  • 2014年02月15日 11:00
  • 866

Java多线程 -- 正确使用Volatile变量

原文http://www.ibm.com/developerworks/cn/java/j-jtp06197.ht
  • fw0124
  • fw0124
  • 2011年08月08日 22:42
  • 3131

银行取款[多线程]{使用volatile修饰共享变量,但此场景并不保证线程同步}

经典例子:老婆(朱丽叶)老公(罗密欧),使用银行卡和存折,或者网银等,同时对同一账户操作的安全问题。 此处用多线程实现,同时取款的模拟实现,使用volatile修饰共享变量,但此场景并不保证线程同步...

[代码安全] 关于全局变量被修改以及volatile的用法

转载地址:http://blog.sina.com.cn/s/blog_5ddb672b0100hi0v.html 今天调一个程序的时候发现一个事情,就是全局变量在中断中被改变,代码...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:1对变量的写操作不依赖于当前值。 2该变量没有包含在具有其他
举报原因:
原因补充:

(最多只允许输入30个字)