Java多线程之内存可见性和原子性:Synchronized和Volatile的比较
【尊重原创,转载请注明出处】http://blog.csdn.net/guyuealian/article/details/52525724
(1)Java所有变量都存储在主内存中
(2)每个线程都有自己独立的工作内存,里面保存该线程的使用到的变量副本(该副本就是主内存中该变量的一份拷贝)
(2)每个线程都有自己独立的工作内存,里面保存该线程的使用到的变量副本(该副本就是主内存中该变量的一份拷贝)
(1)线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接在主内存中读写
(2)不同线程之间无法直接访问其他线程工作内存中的变量,线程间变量值的传递需要通过主内存来完成。
线程1对共享变量的修改,要想被线程2及时看到,必须经过如下2个过程:
(1)把工作内存1中更新过的共享变量刷新到主内存中
(2)将主内存中最新的共享变量的值更新到工作内存2中
(2)不同线程之间无法直接访问其他线程工作内存中的变量,线程间变量值的传递需要通过主内存来完成。
线程1对共享变量的修改,要想被线程2及时看到,必须经过如下2个过程:
(1)把工作内存1中更新过的共享变量刷新到主内存中
(2)将主内存中最新的共享变量的值更新到工作内存2中
可见性与原子性
可见性:一个线程对共享变量的修改,更够及时的被其他线程看到
原子性:即不可再分了,不能分为多步操作。比如赋值或者return。比如"a = 1;"和 "return a;"这样的操作都具有原子性。类似"a += b"这样的操作不具有原子性,在某些JVM中"a += b"可能要经过这样三个步骤:
① 取出a和b
② 计算a+b
③ 将计算结果写入内存
原子性:即不可再分了,不能分为多步操作。比如赋值或者return。比如"a = 1;"和 "return a;"这样的操作都具有原子性。类似"a += b"这样的操作不具有原子性,在某些JVM中"a += b"可能要经过这样三个步骤:
① 取出a和b
② 计算a+b
③ 将计算结果写入内存
(1)Synchronized:保证可见性和原子性
Synchronized能够实现原子性和可见性;在Java内存模型中,synchronized规定,线程在加锁时,先清空工作内存→在主内存中拷贝最新变量的副本到工作内存→执行完代码→将更改后的共享变量的值刷新到主内存中→释放互斥锁。
Synchronized能够实现原子性和可见性;在Java内存模型中,synchronized规定,线程在加锁时,先清空工作内存→在主内存中拷贝最新变量的副本到工作内存→执行完代码→将更改后的共享变量的值刷新到主内存中→释放互斥锁。
(2)Volatile:保证可见性,但不保证操作的原子性
Volatile实现内存可见性是通过store和load指令完成的;也就是对volatile变量执行写操作时,会在写操作后加入一条store指令,即强迫线程将最新的值刷新到主内存中;而在读操作时,会加入一条load指令,即强迫从主内存中读入变量的值。但volatile不保证volatile变量的原子性,例如:
-
Private int Num=0;
-
Num++;//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,不同线程之间无法直接访问其他线程工作内存中的变量
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`变量虽然保证了可见性,但是并不能保证原子性。也就是说,对于复合操作(如`i++`),`volatile`并不能确保这些操作不会被拆分成多个步骤执行。这可能导致数据不一致性问题。 #### 三、...
- **不保证原子性**:虽然volatile可以保证可见性,但不能保证对变量的操作是原子性的,如int类型的++操作就不是原子的。 - **使用场景**:适用于读多写少的场景,例如共享状态标志、配置参数等。 每个demo都可能...
- **非原子性**:需要注意的是,虽然`volatile`关键字提供了可见性和有序性保证,但它并不能保证复合操作的原子性。例如,对于`i++`这样的操作,即使`i`是`volatile`变量,该操作也不是原子性的。 #### 五、使用...
总结来说,Java内存模型通过原子性、有序性和可见性保证了多线程环境下的数据一致性。理解并熟练运用这些概念是编写高效、线程安全的Java代码的基础。在实际开发中,应根据需求选择合适的方式来确保这三个特性,以...
2. volatile只能保证可见性,而synchronized可以保证可见性、原子性和有序性。 3. volatile修饰的变量,不允许线程内部缓存和重排序,而synchronized则可以保证线程之间的同步执行。 四、 Java中的原子类 1. ...
在Java并发编程中,内存可见性和线程安全是核心议题。本文将探讨三个关键概念:过期数据、锁的可见性以及Volatile关键字的作用。首先,我们来看一下“过期数据”这一问题。 1. 过期数据 在并发环境中,当多个线程...
Java多线程编程中,原子性、可见性和有序性是三个关键的概念,它们确保了多线程环境下的正确性。 1. 原子性(Atomicity) 原子性指的是一个操作不可被中断,要么全部执行,要么完全不执行。在Java中,非原生类型的...
然而,并发编程也带来了一系列挑战,如线程安全、可见性、原子性和有序性问题。 **并发理论基础**: 并发编程的核心在于多线程,允许多个执行单元在同一时间执行任务,以充分利用系统资源。在Java中,通过创建...
1. **使用`synchronized`关键字**:它可以确保同一时刻只有一个线程可以执行指定的代码块,从而保证了原子性和可见性。 2. **使用`Lock`接口**:通过显式地锁定和解锁来实现对资源的独占访问。 3. **使用`volatile`...
volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个...
本文档总结了并发编程中的70道面试题及答案,涵盖了线程、synchronized、volatile、CAS、Lock、ReentrantLock等关键概念,旨在帮助开发者更好地理解并发编程的基本概念和机理。 线程间通信: * wait/notify机制:...
volatile 是一个变量修饰符,用于修饰变量,使其具有可见性和原子性。volatile 变量的特点是: * 可见性:volatile 变量的值是最新的,所有线程都可以看到最新的值。 * 原子性:volatile 变量的修改是原子的,一个...
通过对`volatile`关键字的工作原理及其在多线程环境中的应用进行深入探讨,我们可以更好地理解如何利用`volatile`来解决可见性和有序性问题。同时,我们也应该注意到`volatile`并不能保证操作的原子性,因此在需要...
volatile关键字用于保证变量的可见性和有序性。它确保了当一个线程修改了volatile变量的值时,其他线程可以立即看到这个新值,解决了Java内存模型导致的可见性问题。volatile的实现依赖于内存屏障,它确保在屏障前的...
当一个变量被声明为volatile时,它的修改会立即反映到其他线程中,避免了缓存导致的可见性问题。但是,volatile不能保证原子性,对于多线程环境下需要原子性的操作,还需要配合其他同步机制如synchronized。 ...
volatile 只能保证单个变量的原子性和可见性,而 synchronized 可以保证整个代码块的原子性。 9. volatile 实现原理:Java编译器会在读写 volatile 变量时添加内存屏障,保证读写操作的顺序,同时会将 volatile ...
3. synchronized关键字:synchronized提供了更强大的保障,它可以保证代码块或方法的原子性、可见性和有序性。当一个线程进入一个由synchronized修饰的代码块时,其他线程将无法同时进入。 4. 内存屏障:内存屏障是...
7. **对比`synchronized``volatile`**:`synchronized`提供了更强大的同步控制,包括原子性、可见性和有序性。它能保证整个代码块的原子执行,而`volatile`只能保证变量的读写有序性。`volatile`适用于简单共享变量...
9. ** volatile和synchronized的区别**:volatile仅保证可见性和有序性,不保证原子性;而synchronized则提供锁机制,保证了原子性、可见性和有序性。 深入理解Java内存模型,可以帮助开发者避免并发编程中出现的...
- **可见性**:Java提供了多种机制来保证可见性,如使用`volatile`关键字、synchronized块以及final字段。 - **有序性**:`volatile`关键字和synchronized块都可以用来禁止指令重排序,从而保证操作的顺序性。 ### ...