zoukankan      html  css  js  c++  java
  • volatile的原理与技巧,以及与synchronized的区别

    volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

    Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。
    这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。
    而volatile关键字就是提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
    使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。
    由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。
    就跟C中的一样 禁止编译器进行优化~~~~

    Java 语言中的 volatile 变量可以被看作是一种 “程度较轻的 synchronized”;
    与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分。

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

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

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

    • 对变量的写操作不依赖于当前值。(所以volatile 变量不能用作线程安全计数器,增量操作(x++)看上去类似一个单独操作,实际上它是一个由读取-修改-写入操作序列组成的组合操作,必须以原子方式执行,而 volatile 不能提供必须的原子特性。)
    • 该变量没有包含在具有其他变量的不变式中。 
    实际上,这些条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。
     
    正确使用 volatile 的模式:
    1. 状态标志:也许实现 volatile 变量的规范使用仅仅是使用一个布尔状态标志,用于指示发生了一个重要的一次性事件,例如完成初始化或请求停机。 这种类型的状态标记的一个公共特性是:通常只有一种状态转换;例如 从 false 转换为 true,然后程序停止。
     
     

    volatile和synchronized的区别:

    1.volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;

    synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。

    2.volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的

    3.volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性

    4.volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。

    5.volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化

    // 这个方法会让线程阻塞在 wait() 函数上,不往下执行,但是这时候其他线程还是能获得mLock锁的,并进入临界区执行代码的

    synchronized (mLock) {
        mLock.wait();
     }

    // 比如 调用notifyAll 唤醒线程的时候也需要先获得该锁

    synchronized (mLock) {
           mLock.notifyAll();
    }

    A waiting thread can be sent interrupt() to cause it to prematurely stop waiting, so wait should be called in a loop to check that the condition that has been waited for has been met before continuing.

    While the thread waits, it gives up ownership of this object's monitor. When it is notified (or interrupted), it re-acquires the monitor before it starts running.

     

  • 相关阅读:
    Windows 8.1 Visual Studio 2013 OpenGL 配置
    panic 和 recover的区别
    Beego操作数据库
    sql 中 inner join、left join 和 right join的区别
    Go实现网页爬虫
    SqlServer中 不区分大小写 和 全半角的写法
    循环语句
    switch语句
    iota枚举
    关于Go开发工具(LiteIDE)
  • 原文地址:https://www.cnblogs.com/zijianlu/p/2265995.html
Copyright © 2011-2022 走看看