volatile 和 synchronized 的比较

本文详细解析了Java中volatile与synchronized的区别与应用场景。volatile确保变量在多线程环境中的可见性,但不能保证操作的原子性;synchronized通过锁定机制确保代码块的原子性和排他访问。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1,volatile
   它所修饰的变量不保留拷贝,直接访问主内存中的。
   在 Java 内存模型中,有 main memory,每个线程也有自己的 memory (例如寄存器)。为了性能,一个线程会在自己的 memory 中保持要访问的变量的副本。这样就会出现同一个变 量在某个瞬间,在一个线程的 memory 中的值可能与另外一个线程 memory 中的值,或者 main memory 中的值不一致的情况。一个变量声明为 volatile,就意味着这个变量是随时会被其他线程修改的,因此不能将它 cache 在线程memory 中。



2,synchronized

      当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

     一、当两个并发线程访问同一个对象 objec t中的这个 synchronized(this) 同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

     二、然而,当一个线程访问 object 的一个 synchronized(this) 同步代码块时,另一个线程仍然可以访问该 object 中的非 synchronized(this)同步代码块。

     三、尤其关键的是,当一个线程访问 object 的一个 synchronized(this) 同步代码块时,其他线程对 object 中所有其它 synchronized(this) 同步代码块的访问将被阻塞。

     四、当一个线程访问 object 的一个 synchronized(this) 同步代码块时,它就获得了这个 object 的对象锁。结果,其它线程对该 object 对象所有同步代码部分的访问都被暂时阻塞。

     五、以上规则对其它对象锁同样适用.

 

区别:

 一、volatile 是变量修饰符,而 synchronized 则作用于一段代码或方法。

 二、volatile 只是在线程内存和“主”内存间同步某个变量的值;而 synchronized 通过锁定和解锁某个监视器同步所有变量的值。显然synchronized 要比 volatile 消耗更多资源。 

 三、volatile 不能保证操作的原子性。synchronized 可以保证操作的原子性。

那么请参考如下:

volatile 不能保证操作的原子性。synchronized 可以保证操作的原子性。我认为这是他们最大的区别。

此外 volatile 是变量修饰符,而 synchronized 是要修饰一段代码或者方法

 

那么volatile是干什么的呢?

volatile是变量修饰符。被它修饰的变量不会在线程中保留备份(注:可以这么理解,但是其内部机制应该还是保留了备份的,只不过是通过特殊的机制实时的将线程中变量的值更新到i)。这样就不会出现上述线程A B 和主内存的值不一样的情况。

但是注意:volatile并不能保证操作的原子性。即使只是i++,实际上也是由多个原子操作组成:read i; inc; write i,假如多个线程同时执行i++,依然可能出现写入脏数据的情况。它只不过是保证各个线程操作的是同一块儿内存而已。

synchronized 为什么能保证操作的原子性呢?

这得从它的工作机制说起,很明显 synchronized 能够保证被它修饰的一段代码同一个时刻只被同一个线程执行。

事实上它同步代码的过程也在同步内存。

假如有如下代码:

public class Test implements Runnable{

      private int i=1;

      public void run(){

          operate();

      }


      public synchronized void operate(){

            i = 2;

     }

}

当线程A执行方法operate的时候将进行如下步骤:

1.线程请求this的锁,如果锁被其他的线程占用就进入阻塞状态直到锁被释放。

2.线程内存中缓存的数据被清除,从主内存中read/load读入最新的值。

3.synchronized内的代码块被执行,在此执行过程中其对应的主内存不会被其他的线程使用。

4.执行完毕释放锁。

 

水平有限,若有问题请留言交流!

互相学习,共同进步 :)转载请注明出处谢谢!

转载于:https://my.oschina.net/hp2017/blog/1561018

<think>我们正在讨论Javavolatilesynchronized关键字的区别。根据提供的引用,我们可以总结如下:1.**volatile关键字**:-只能用于变量(不能用于方法或代码块)。-保证变量的可见性:当一个线程修改了volatile变量的值,新值对其他线程是立即可见的。-不保证原子性:例如,自增操作(i++)不是原子操作,即使变量是volatile,多线程同时修改仍可能导致问题。-使用场景:通常用于状态标志(如引用[4]中的示例)或者一次性发布(如单例模式的双重检查锁定)。2.**synchronized关键字**:-可以修饰方法(实例方法静态方法)或代码块。-保证可见性原子性:进入同步代码块前,会清空工作内存,从主内存重新读取变量;退出同步代码块时,会将修改后的变量刷新回主内存。同时,同步代码块内的操作是原子执行的。-使用场景:需要保证多个线程访问共享资源时的同步,例如对共享变量的复合操作。**区别总结**:-**作用范围**:volatile仅能用于变量,而synchronized可以用于方法代码块。-**性能**:volatile是轻量级的同步机制,性能通常比synchronized好(引用[2])。-**可见性**:两者都能保证可见性,但synchronized还会保证原子性。-**原子性**:volatile不保证原子性,synchronized保证原子性。-**使用场景**:volatile适用于一个线程写、多个线程读的场景;synchronized适用于多个线程读写共享资源的场景。**示例说明**:-volatile示例(状态标志):```javaprivatevolatilebooleanrunning=true;```一个线程修改`running`为false,另一个线程会立即看到并停止循环。-synchronized示例(单例模式的双重检查锁定):```javaprivatestaticvolatileSingletoninstance;publicstaticSingletongetInstance(){if(instance==null){synchronized(Singleton.class){if(instance==null){instance=newSingleton();}}}returninstance;}```这里使用volatile确保`instance`的可见性,同时使用synchronized保证创建实例的原子性。§§相关问题§§1.为什么volatile不能保证原子性?请举例说明。2.在单例模式的双重检查锁定中,为什么需要同时使用volatilesynchronized?3.除了状态标志单例模式,volatile还有哪些典型应用场景?4.synchronized锁的粒度有哪些?如何选择?</think>### Java中`volatile``synchronized`关键字的区别 #### 1. **核心功能差异** - **`volatile`**: - 仅保证变量的**可见性**:当一个线程修改`volatile`变量时,新值立即对其他线程可见(强制刷新主内存)[^2][^4]。 - **不保证原子性**:例如`count++`(非原子操作)在多线程下仍可能出错。 - 适用场景:独立状态标志(如`boolean running`)或单次发布(如单例模式的双重检查锁定)[^4]。 - **`synchronized`**: - 保证**可见性原子性**:通过锁机制确保代码块/方法内操作不可分割,且操作结果对所有线程立即可见[^2][^4]。 - 适用场景:多线程共享资源的复合操作(如转账、计数器累加)。 #### 2. **作用范围** - **`volatile`**: - **仅修饰变量**(如`private volatile int count;`)[^2]。 - **`synchronized`**: - 修饰**代码块**(锁定指定对象): ```java synchronized (lockObject) { /* 临界区 */ } ``` - 修饰**实例方法**(锁定当前对象): ```java public synchronized void method() { ... } ``` - 修饰**静态方法**(锁定类对象): ```java public static synchronized void staticMethod() { ... } ```[^3] #### 3. **性能差异** - **`volatile`**: - 轻量级同步,无锁操作,性能开销小(仅读写内存屏障)[^2]。 - **`synchronized`**: - 重量级同步,涉及锁竞争、上下文切换,性能开销较大(JDK优化后如偏向锁/自旋锁可缓解)[^2]。 #### 4. **典型应用场景对比** | **场景** | **`volatile`适用性** | **`synchronized`适用性** | |------------------------|----------------------------|--------------------------------| | 状态标志(如`boolean running`) | ✅(一写多读) | ⚠️(过度开销) | | 单例模式(双重检查锁定) | ✅(配合`synchronized`) | ✅(直接锁定方法) | | 计数器累加(`count++`) | ❌(不保证原子性) | ✅(保证原子操作) | | 复合操作(如转账) | ❌ | ✅ | #### 5. **示例说明** - **`volatile`可见性示例**: ```java public class VolatileExample { private volatile boolean running = true; // 状态标志 public void stop() { running = false; } // 修改后立即可见 public void run() { while (running) { /* 执行任务 */ } // 其他线程立刻感知到变化 } } ```[^4] - **`synchronized`原子性示例**: ```java public class Counter { private int count = 0; public synchronized void increment() { count++; // 原子操作 } } ``` #### 6. **总结对比表** | **特性** | `volatile` | `synchronized` | |------------------|-----------------------------|------------------------------| | **可见性** | ✅ | ✅ | | **原子性** | ❌ | ✅ | | **作用对象** | 变量 | 代码块/方法 | | **性能开销** | 低 | 高 | | **适用场景** | 状态标志、单次发布 | 复合操作、共享资源竞争 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值