`
85977328
  • 浏览: 1871499 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java并发(二十六)正确使用Volatile变量

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

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

与锁相比,Volatile 变量是一种非常简单但同时又非常脆弱的同步机制,它在某些情况下将提供优于锁的性能和伸缩性。如果严格遵循 volatile 的使用条件
—— 即变量真正独立于其他变量和自己以前的值
—— 在某些情况下可以使用 volatile 代替 synchronized 来简化代码。
然而,使用 volatile 的代码往往比使用锁的代码更加容易出错。当你在线程中读取一个非volatile变量时,你最终能读取它更新的那个值。

原子访问
在编程过程中,原子操作是指所有操作都同时发生。原子操作不能被中途打断:要么全做,要么不做。原子操作在完成前不会有看得见的副作用。

我们发现像c++这样的增量表达式,并没有描述原子操作。即使是非常简单的表达式也能够定义成能被分解为其他操作的复杂操作。然而,有些操作你可以定义为原子的:

对引用变量和大部分基本类型变量(除long和double之外)的读写是原子的。
对所有声明为volatile的变量(包括long和double变量)的读写是原子的。

原子操作不会交错,于是可以放心使用,不必担心线程干扰。然而,这并不能完全消除原子操作上的同步,因为内存一致性错误仍可能发生。使用volatile变量可以降低内存一致性错误的风险,因为对volatile变量的任意写操作,对于后续在该变量上的读操作建立了happens-before关系。这意味着volatile变量的修改对于其他线程总是可见的。更重要的是,这同时也意味着当一个线程读取一个volatile变量时,它不仅能看到该变量最新的修改,而且也能看到致使该改变发生的代码的副效应。

使用简单的原子变量访问比通过同步代码来访问更高效,但是需要程序员更加谨慎以避免内存一致性错误。至于这额外的付出是否值得,得看应用的大小和复杂度。

java.util.concurrent包中的一些类提供了一些不依赖同步机制的原子方法。

常规用法
模式 #1:状态标志
volatile boolean shutdownRequested;
...
public void shutdown() { shutdownRequested = true; }
public void doWork() { 
    while (!shutdownRequested) { 
        // do stuff
    }
}


模式 #2:一次性安全发布(one-time safe publication)
public class BackgroundFloobleLoader {
    public volatile Flooble theFlooble;

    public void initInBackground() {
        // do lots of stuff
        theFlooble = new Flooble();  // this is the only write to theFlooble
    }
}

public class SomeOtherClass {
    public void doWork() {
        while (true) { 
            // do some stuff...
            // use the Flooble, but only if it is ready
            if (floobleLoader.theFlooble != null) 
                doSomething(floobleLoader.theFlooble);
        }
    }
}


模式 #3:独立观察(independent observation)
public class UserManager {
    public volatile String lastUser;

    public boolean authenticate(String user, String password) {
        boolean valid = passwordIsValid(user, password);
        if (valid) {
            User u = new User();
            activeUsers.add(u);
            lastUser = user;
        }
        return valid;
    }
}


模式 #4:“volatile bean” 模式
@ThreadSafe
public class Person {
    private volatile String firstName;
    private volatile String lastName;
    private volatile int age;

    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public int getAge() { return age; }

    public void setFirstName(String firstName) { 
        this.firstName = firstName;
    }

    public void setLastName(String lastName) { 
        this.lastName = lastName;
    }

    public void setAge(int age) { 
        this.age = age;
    }
}


模式 #5:开销较低的读-写锁策略
@ThreadSafe
public class CheesyCounter {
    // Employs the cheap read-write lock trick
    // All mutative operations MUST be done with the 'this' lock held
    @GuardedBy("this") private volatile int value;

    public int getValue() { return value; }

    public synchronized int increment() {
        return value++;
    }
}
分享到:
评论

相关推荐

    Java并发编程(5)volatile变量修饰符-意料之外

    Java并发编程(5)volatile变量修饰符—意料之外的问题(含代码)编程开发技术共6页.pdf.zip

    Java并发编程实战

    3.1.4 Volatile变量 3.2 发布与逸出 3.3 线程封闭 3.3.1 Ad-hoc线程封闭 3.3.2 栈封闭 3.3.3 ThreadLocal类 3.4 不变性 3.4.1 Final域 3.4.2 示例:使用Volatile类型来发布不可变对象 3.5 安全发布 3.5.1...

    Java并发编程的艺术.md

    第二章 Java并发机制的底层实现原理 volatile的两条实现原则: 1. Lock前缀指令会引起处理器缓存回写到内存 2. 一个处理器的缓存回写到内存会导致其他处理器的缓存无效。 volatile的使用优化:共享变量会被...

    Java并发编程之volatile变量介绍

    主要介绍了Java并发编程之volatile变量介绍,volatile提供了弱同步机制,用来确保将变量更新通知到其它线程,需要的朋友可以参考下

    Java并发volatile关键字.docx

    synchronized是阻塞式同步,在线程...这个实际对普通变量没有规定的,而针对volatile修饰的变量给Java虚拟机特殊的约定,线程对volatile变量的修改会立刻被其他线程所感知,即不会出现数据脏读,从而保证数据的可见性。

    Java 并发编程实战

    3.1.4 Volatile变量 3.2 发布与逸出 3.3 线程封闭 3.3.1 Ad-hoc线程封闭 3.3.2 栈封闭 3.3.3 ThreadLocal类 3.4 不变性 3.4.1 Final域 3.4.2 示例:使用Volatile类型来发布不可变对象 3.5 安全发布 3.5.1...

    并发编程基础知识,java内存模型及多线程、volatile

    Java内存模型,即:JMM。当程序执⾏并⾏操作时,如果对数据的访问和操作不加以控制,那么必 然会对程序的正确性造成破坏。因此,我们需要在深⼊了解并⾏机制的前提下,再定义⼀种规则, 来保证多个线程间可以有效地...

    Java并发编程培训(阿里巴巴)

    volatile关键字: ...脆弱的volatile的使用条件: 1:写入变量不依赖变量的当前值,或者能够保证只有单一的线程修改变量的值; 2:变量不需要和其他变量共同参与不变约束; 3:访问变量时不需要其他原因需要加锁。

    java 高并发中volatile的实现原理

    主要介绍了java 高并发中volatile的实现原理的相关资料,在多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”,需要的朋友...

    并发编程实践,全面介绍基础知识、JVM同步原语、线程安全、低级并发工具、线程安全容器、高级线程协作工具、Executor部分等

    详细介绍java并发编程相关知识: 基础知识   并发与并行   Java并发演进历史   Java并发模型   线程模型   存储模型 JVM同步原语 volatile CAS 线程安全   保护“共享数据” 低级并发工具   原子变量   锁...

    Java中volatile关键字的含义

    在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候可以万事大吉。  Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 ...

    java多线程编程总结

    Java线程:volatile关键字 Java线程:新特征-线程池 Java线程:新特征-有返回值的线程 Java线程:新特征-锁(上) Java线程:新特征-锁(下) Java线程:新特征-信号量 Java线程:新特征-阻塞队列 Java线程:新特征-...

    Java并发:volatile内存可见性和指令重排

     Java内存模型规定,对于多个线程共享的变量,存储在主内存当中,每个线程都有自己独立的工作内存(比如CPU的寄存器),线程只能访问自己的工作内存,不可以访问其它线程的工作内存。  工作内存中保存了主内存...

    Java多线程编程总结

    Java线程:volatile关键字 Java线程:新特征-线程池 Java线程:新特征-有返回值的线程 Java线程:新特征-锁(上) Java线程:新特征-锁(下) Java线程:新特征-信号量 Java线程:新特征-阻塞队列 Java线程:...

    java多线程笔记

    Java线程:线程栈模型与线程的变量 12 Java线程:线程的调度-休眠 13 Java线程:线程的调度-优先级 16 Java线程:线程的调度-让步 19 Java线程:线程的调度-合并 22 Java线程:线程的调度-终止线程 25 Java线程:...

    Java多线程并发编程 Volatile关键字

    volatile 关键字是一个神秘的关键字,也许在 J2EE 上的 JAVA 程序员会了解多一点,但在 Android 上的 JAVA 程序员大多不了解...只要稍了解不当就好容易导致一些并发上的错误发生,例如好多人把 volatile 理解成变量的锁

    java并发理论摘要

    数据依赖 所谓的数据依赖是指,如果多个java指令之间顺序变化会...比如控制依赖(if),满足if条件才会执行里面的操作,里面的操作是使用共享变量做运算,为了提高指令的并发,会先做运算把结果保存在缓存在,等if真的

Global site tag (gtag.js) - Google Analytics