`

内存可见性和原子性:Synchronized和Volatile的比较

 
阅读更多

Java多线程之内存可见性和原子性:Synchronized和Volatile的比较

    【尊重原创,转载请注明出处】http://blog.csdn.net/guyuealian/article/details/52525724
 
     在说明Java多线程内存可见性之前,先来简单了解一下Java内存模型。
     (1)Java所有变量都存储在主内存中
     (2)每个线程都有自己独立的工作内存,里面保存该线程的使用到的变量副本(该副本就是主内存中该变量的一份拷贝)

   (1)线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接在主内存中读写
   (2)不同线程之间无法直接访问其他线程工作内存中的变量,线程间变量值的传递需要通过主内存来完成。
线程1对共享变量的修改,要想被线程2及时看到,必须经过如下2个过程:
   (1)把工作内存1中更新过的共享变量刷新到主内存中
   (2)将主内存中最新的共享变量的值更新到工作内存2中

可见性与原子性
   可见性:一个线程对共享变量的修改,更够及时的被其他线程看到
   原子性:即不可再分了,不能分为多步操作。比如赋值或者return。比如"a = 1;"和 "return a;"这样的操作都具有原子性。类似"a += b"这样的操作不具有原子性,在某些JVM中"a += b"可能要经过这样三个步骤:
① 取出a和b
② 计算a+b
③ 将计算结果写入内存
 
(1)Synchronized:保证可见性和原子性
    Synchronized能够实现原子性和可见性;在Java内存模型中,synchronized规定,线程在加锁时,先清空工作内存→在主内存中拷贝最新变量的副本到工作内存→执行完代码→将更改后的共享变量的值刷新到主内存中→释放互斥锁

(2)Volatile:保证可见性,但不保证操作的原子性
    Volatile实现内存可见性是通过store和load指令完成的;也就是对volatile变量执行写操作时,会在写操作后加入一条store指令,即强迫线程将最新的值刷新到主内存中;而在读操作时,会加入一条load指令,即强迫从主内存中读入变量的值。但volatile不保证volatile变量的原子性,例如:
  1.  
    Private int Num=0;
  2.  
    Num++;//Num不是原子操作
    Num不是原子操作,因为其可以分为:读取Num的值,将Num的值+1,写入最新的Num的值。
    对于Num++;操作,线程1和线程2都执行一次,最后输出Num的值可能是:1或者2
   【解释输出结果1的解释:当线程1执行Num++;语句时,先是读入Num的值为0,倘若此时让出CPU执行权,线程获得执行,线程2会重新从主内存中,读入Num的值还是0,然后线程2执行+1操作,最后把Num=1刷新到主内存中; 线程2执行完后,线程1由开始执行,但之前已经读取的Num的值0,所以它还是在0的基础上执行+1操作,也就是还是等于1,并刷新到主内存中。所以最终的结果是1
    一般在多线程中使用volatile变量,为了安全,对变量的写入操作不能依赖当前变量的值:如Num++或者Num=Num*5这些操作。

(3)Synchronized和Volatile的比较
    1)Synchronized保证内存可见性和操作的原子性
    2)Volatile只能保证内存可见性
    3)Volatile不需要加锁,比Synchronized更轻量级,并不会阻塞线程(volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。)
    4)volatile标记的变量不会被编译器优化,而synchronized标记的变量可以被编译器优化(如编译器重排序的优化).
    5)volatile是变量修饰符,仅能用于变量,而synchronized是一个方法或块的修饰符。
      volatile本质是在告诉JVM当前变量在寄存器中的值是不确定的,使用前,需要先从主存中读取,因此可以实现可见性。而对n=n+1,n++等操作时,volatile关键字将失效,不能起到像synchronized一样的线程同步(原子性)的效果。
 
【参考资料】《细说Java多线程之内存可见性》http://www.imooc.com/video/6775(含视频和代码)
【相关习题】
(1)下列说法不正确的是()
A.当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。
B.当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
C.当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问不会被阻塞。
D.当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
答案:C,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将会被阻塞。
(2)下面叙述错误的是:
A.通过synchronized和volatile都可以实现可见性
B.不同线程之间可以直接访问其他线程工作内存中的变量
C.线程对共享变量的所有操作都必须在自己的工作内存中进行
D.所有的变量都存储在主内存中
答案:B,不同线程之间无法直接访问其他线程工作内存中的变量

--------------------- 本文来自 pan_jinquan 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/guyuealian/article/details/52525724?utm_source=copy 

分享到:
评论

相关推荐

    volatile和synchronized的区别

    - **非原子性**:`volatile`变量虽然保证了可见性,但是并不能保证原子性。也就是说,对于复合操作(如`i++`),`volatile`并不能确保这些操作不会被拆分成多个步骤执行。这可能导致数据不一致性问题。 #### 三、...

    并发编程一之synchronized和volatile.rar

    - **不保证原子性**:虽然volatile可以保证可见性,但不能保证对变量的操作是原子性的,如int类型的++操作就不是原子的。 - **使用场景**:适用于读多写少的场景,例如共享状态标志、配置参数等。 每个demo都可能...

    Java并发编程:volatile关键字解析

    - **非原子性**:需要注意的是,虽然`volatile`关键字提供了可见性和有序性保证,但它并不能保证复合操作的原子性。例如,对于`i++`这样的操作,即使`i`是`volatile`变量,该操作也不是原子性的。 #### 五、使用...

    Java内存模型--原子性;有序性;可见性1

    总结来说,Java内存模型通过原子性、有序性和可见性保证了多线程环境下的数据一致性。理解并熟练运用这些概念是编写高效、线程安全的Java代码的基础。在实际开发中,应根据需求选择合适的方式来确保这三个特性,以...

    java里的volatile关键字详解

    2. volatile只能保证可见性,而synchronized可以保证可见性、原子性和有序性。 3. volatile修饰的变量,不允许线程内部缓存和重排序,而synchronized则可以保证线程之间的同步执行。 四、 Java中的原子类 1. ...

    Java并行(3):可见性重访之锁、Volatile与原子变量1

    在Java并发编程中,内存可见性和线程安全是核心议题。本文将探讨三个关键概念:过期数据、锁的可见性以及Volatile关键字的作用。首先,我们来看一下“过期数据”这一问题。 1. 过期数据 在并发环境中,当多个线程...

    Java多线程中提到的原子性和可见性、有序性1

    Java多线程编程中,原子性、可见性和有序性是三个关键的概念,它们确保了多线程环境下的正确性。 1. 原子性(Atomicity) 原子性指的是一个操作不可被中断,要么全部执行,要么完全不执行。在Java中,非原生类型的...

    java并发理论基础、可见性、原子性、有序性详解

    然而,并发编程也带来了一系列挑战,如线程安全、可见性、原子性和有序性问题。 **并发理论基础**: 并发编程的核心在于多线程,允许多个执行单元在同一时间执行任务,以充分利用系统资源。在Java中,通过创建...

    14、深入理解并发可见性、有序性、原子性与JMM内存模型(1).pdf

    1. **使用`synchronized`关键字**:它可以确保同一时刻只有一个线程可以执行指定的代码块,从而保证了原子性和可见性。 2. **使用`Lock`接口**:通过显式地锁定和解锁来实现对资源的独占访问。 3. **使用`volatile`...

    Java线程:volatile关键字

    volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个...

    并发编程 70 道面试题及答案.docx

    本文档总结了并发编程中的70道面试题及答案,涵盖了线程、synchronized、volatile、CAS、Lock、ReentrantLock等关键概念,旨在帮助开发者更好地理解并发编程的基本概念和机理。 线程间通信: * wait/notify机制:...

    java中volatile和synchronized的区别与联系

    volatile 是一个变量修饰符,用于修饰变量,使其具有可见性和原子性。volatile 变量的特点是: * 可见性:volatile 变量的值是最新的,所有线程都可以看到最新的值。 * 原子性:volatile 变量的修改是原子的,一个...

    深入解析volatile关键字:保障多线程下的数据一致性与可见性.pdf

    通过对`volatile`关键字的工作原理及其在多线程环境中的应用进行深入探讨,我们可以更好地理解如何利用`volatile`来解决可见性和有序性问题。同时,我们也应该注意到`volatile`并不能保证操作的原子性,因此在需要...

    synchronized ReentrantLock volatile Atomic 原理分析.docx

    volatile关键字用于保证变量的可见性和有序性。它确保了当一个线程修改了volatile变量的值时,其他线程可以立即看到这个新值,解决了Java内存模型导致的可见性问题。volatile的实现依赖于内存屏障,它确保在屏障前的...

    并发编程之JMM&synchronized&volatile详解.pdf

    当一个变量被声明为volatile时,它的修改会立即反映到其他线程中,避免了缓存导致的可见性问题。但是,volatile不能保证原子性,对于多线程环境下需要原子性的操作,还需要配合其他同步机制如synchronized。 ...

    java内存模型.zip

    volatile 只能保证单个变量的原子性和可见性,而 synchronized 可以保证整个代码块的原子性。 9. volatile 实现原理:Java编译器会在读写 volatile 变量时添加内存屏障,保证读写操作的顺序,同时会将 volatile ...

    深入理解Java内存模型(二)共3页.pdf.zip

    3. synchronized关键字:synchronized提供了更强大的保障,它可以保证代码块或方法的原子性、可见性和有序性。当一个线程进入一个由synchronized修饰的代码块时,其他线程将无法同时进入。 4. 内存屏障:内存屏障是...

    多方面解读Java中的volatile关键字.rar

    7. **对比`synchronized``volatile`**:`synchronized`提供了更强大的同步控制,包括原子性、可见性和有序性。它能保证整个代码块的原子执行,而`volatile`只能保证变量的读写有序性。`volatile`适用于简单共享变量...

    java内存模型深入了解

    9. ** volatile和synchronized的区别**:volatile仅保证可见性和有序性,不保证原子性;而synchronized则提供锁机制,保证了原子性、可见性和有序性。 深入理解Java内存模型,可以帮助开发者避免并发编程中出现的...

    深入Java内存模型:揭秘并发编程的基石

    - **可见性**:Java提供了多种机制来保证可见性,如使用`volatile`关键字、synchronized块以及final字段。 - **有序性**:`volatile`关键字和synchronized块都可以用来禁止指令重排序,从而保证操作的顺序性。 ### ...

Global site tag (gtag.js) - Google Analytics