volatile和synchronized的区别与联系

本文对比了volatile和synchronized在Java并发编程中的作用。volatile确保变量在各线程间可见,但不保证原子性;synchronized能保证变量的可见性和原子性,并可通过锁机制控制线程访问。此外还介绍了两者在性能上的差异。

这个可能是最好的对比volatilesynchronized作用的文章了。volatile是一个变量修饰符,而synchronized是一个方法或块的修饰符。所以我们使用这两种关键字来指定三种简单的存取变量的方式。

int i1;                      
int geti1() {
	return i1;
}

volatile int i2;                       
int geti2() {
	return i2;
}

int i3;         
synchronized int geti3() {
	return i3;
}

geti1()当前线程中立即获取在i1变量中的值。线程可以获得变量的本地拷贝,而所获得的变量的值并不一定与其他线程所获得的值相同。特别是,如果其他的线程修改了i1的值,那么当前线程获得的i1的值可能与修改后的值有所差别。实际上,Java有一种主内存的机制,使用一个主内存来保存变量当前的正确的值。线程将变量的值拷贝到自己独立的内存中,而这些线程的内存拷贝可能与主内存中的值不同。所以实际当中可能发生这样的情况,在主内存中i1的值为1,线程1和线程2都更改了i1,但是却没把更新的值传回给主内存或其他线程中,那么可能在线程1i1的值为2,线程2i1的值却为3

另一方面,geti2()可以有效的从主内存中获取i2的值。一个volatile类型的变量不允许线程从主内存中将变量的值拷贝到自己的存储空间。因此,一个声明为volatile类型的变量将在所有的线程中同步的获得数据,不论你在任何线程中更改了变量,其他的线程将立即得到同样的结果。由于线程存取或更改自己的数据拷贝有更高的效率,所以volatile类型变量在性能上有所消耗。

那么如果volatile变量已经可以使数据在线程间同步,那么synchronizes用来干什么呢?两者有两方面的不同。首先,synchronized获取和释放由监听器控制的锁,如果两个线程都使用一个监听器(即相同对象锁),那么监听器可以强制在一个时刻只有一个线程能处理代码块,这是最一般的同步。另外,synchronized还能使内存同步。在实际当中,synchronized使得所有的线程内存与主内存相同步。所以geti3()的执行过程如下:

1.线程从监听器获取对象的锁。(这里假设监听器非锁,否则线程只有等到监听器解锁才能获取对象锁)

2.线程内存更新所有的变量,也就是说他将读取主内存中的变量使自己的变量保证有效。(JVM会使用一个“脏”标志来最优化过程,使得仅仅具有“脏”标志变量被更新。详细的情况查询JAVA规范的17.9)

3.代码块被执行(在这个例子中,设置返回值为刚刚从主内存重置的i3当前的值。)

4.任何变量的变更将被写回到主内存中。但是这个例子中geti3()没有什么变化。

5.线程释放对象的锁给监听器。

所以volatile只能在线程内存和主内存之间同步一个变量的值,而synchronized则同步在线程内存和主内存之间的所有变量的值,并且通过锁住和释放监听器来实现。显然,synchronized在性能上将比volatile更加有所消耗。

 

=============关于两者的区别===================

1.volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住;
2.volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的;
3.volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性;
4.volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞;
5.volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。

红字体部分的原因如下:
线程A修改了变量还没结束时,另外的线程B可以看到已修改的值,而且可以修改这个变量,而不用等待A释放锁,因为Volatile 变量没上锁


<think>好的,用户想了解Java中volatilesynchronized区别,包括它们的用法适用场景。首先,我需要回顾一下这两个关键字的基本概念。 根据引用[1],volatilesynchronized都用于多线程编程,但机制不同。volatile主要解决变量可见性问题,确保一个线程的修改对其他线程立即可见。而synchronized则提供互斥访问,确保代码块或方法在同一时间只有一个线程执行,同时保证可见性原子性。 接下来,引用[3]提到,volatile只能修饰变量,而synchronized可以修饰方法代码块。volatile不保证原子性,比如i++这样的操作,即使变量是volatile的,也需要synchronized来保证整个操作的原子性。而synchronized通过锁机制,既保证了可见性,也确保了原子性。 然后,引用[2]中的代码示例展示了synchronized的可重入性,一个线程在持有锁的情况下可以调用同一对象的其他synchronized方法。这说明synchronized在复杂同步场景中的灵活性,而volatile无法实现这样的功能。 引用[4]对比了volatilestatic的区别,但这里更相关的点是volatile的可见性synchronized的同步性。性能方面,volatile是轻量级的,性能开销较小,而synchronized在JDK优化后性能有所提升,但仍有较高开销,尤其是在竞争激烈的情况下。 现在需要整理这些信息,明确两者的区别,包括作用范围、保证的特性、性能差异使用场景。要确保回答结构清晰,分点说明,并给出实际例子帮助用户理解。 用户可能的问题包括:什么时候该用volatile而不是synchronized?如何选择这两个关键字?或者它们的底层实现原理是什么?需要准备好相关问题,引导用户进一步学习。 最后,检查引用是否正确,确保每个引用标识对应正确的来源,避免混淆。同时,按照用户要求,使用中文回答,数学公式用$...$格式,但这里可能用不上。保持语言简洁明了,避免技术术语过多,必要时用例子说明。</think>### Java中volatilesynchronized区别解析 #### 一、核心区别对比 | **特性** | **volatile** | **synchronized** | |----------------------|-------------------------------------------|-------------------------------------------| | **作用范围** | 仅修饰变量(如:`volatile int x;`) | 修饰代码块或方法(如:`synchronized void method()`) | | **可见性保证** | 强制线程从主内存读取最新值[^3] | 通过锁机制保证可见性[^1] | | **原子性保证** | 不保证(例如`i++`操作仍不安全)[^3] | 保证(锁内操作不可分割)[^3] | | **性能开销** | 轻量级(仅内存屏障)[^3] | 较高(涉及锁竞争上下文切换)[^3] | | **可重入性** | 不涉及 | 支持(线程可重复获取同一锁)[^2] | #### 二、典型场景分析 1. **volatile适用场景** - **状态标志位**:如多线程中控制循环退出的标志 ```java volatile boolean running = true; void stop() { running = false; } // 其他线程可立即看到修改 ``` - **单次写入的共享变量**:如配置加载完成后标记为已初始化[^3] 2. **synchronized适用场景** - **复合操作**:需要原子性保证的操作(如计数器累加) ```java public synchronized void increment() { count++; // 保证原子性 } ``` - **对象状态一致性**:需要同时修改多个关联变量时(如银行转账操作)[^2] #### 三、底层机制差异 - **volatile**:通过插入内存屏障(Memory Barrier)禁止指令重排序,强制写操作直接刷新到主内存,读操作从主内存读取[^3] - **synchronized**:基于Monitor锁机制(对象头中的Mark Word),通过`monitorenter`/`monitorexit`指令实现,包含偏向锁、轻量级锁、重量级锁的升级过程[^1] #### 四、综合使用建议 1. **优先考虑volatile**:当仅需保证可见性且操作为原子性(如boolean赋值)时 2. **必须使用synchronized**:涉及非原子操作(如`i++`)或需要互斥访问资源时 3. **组合使用**:某些场景下可结合两者(如双重检查锁单例模式) ```java class Singleton { private volatile static Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } ```
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值