volatile和synchronized比较比较

volatile关键字有什么用?
  恐怕比较一下volatile和synchronized的不同是最容易解释清楚的。volatile是变量修饰符,而synchronized则作用于一段代码或方法;看如下三句get代码

int i1; int geti1() {return i1;}
volatile int i2; int geti2() {return i2;}
int i3; synchronized int geti3() {return i3;}

geti1()得到存储在当前线程中i1的数值。多个线程有多个i1变量拷贝,而且这些i1之间可以互不相同。换句话说,另一个线程可能已经改变了它线程内的i1值,而这个值可以和当前线程中的i1值不相同。事实上,Java有个思想叫“主”内存区域,这里存放了变量目前的“准确值”。每个线程可以有它自己的变量拷贝,而这个变量拷贝值可以和“主”内存区域里存放的不同。因此实际上存在一种可能:“主”内存区域里的i1值是1,线程1里的i1值是2,线程2里的i1值是3——这在线程1和线程2都改变了它们各自的i1值,而且这个改变还没来得及传递给“主”内存区域或其他线程时就会发生。
  而geti2()得到的是“主”内存区域的i2数值。用volatile修饰后的变量不允许有不同于“主”内存区域的变量拷贝。换句话说,一个变量经volatile修饰后在所有线程中必须是同步的;任何线程中改变了它的值,所有其他线程立即获取到了相同的值。理所当然的,volatile修饰的变量存取时比一般变量消耗的资源要多一点,因为线程有它自己的变量拷贝更为高效。
  既然volatile关键字已经实现了线程间数据同步,又要synchronized干什么呢?呵呵,它们之间有两点不同。首先,synchronized获得并释放监视器——如果两个线程使用了同一个对象锁,监视器能强制保证代码块同时只被一个线程所执行——这是众所周知的事实。但是,synchronized也同步内存:事实上,synchronized在“主”内存区域同步整个线程的内存。因此,执行geti3()方法做了如下几步:
1. 线程请求获得监视this对象的对象锁(假设未被锁,否则线程等待直到锁释放)
2. 线程内存的数据被消除,从“主”内存区域中读入(Java虚拟机能优化此步)
3. 代码块被执行
4. 对于变量的任何改变现在可以安全地写到“主”内存区域中(不过geti3()方法不会改变变量值)
5. 线程释放监视this对象的对象锁
  因此volatile只是在线程内存和“主”内存间同步某个变量的值,而synchronized通过锁定和解锁某个监视器同步所有变量的值。显然synchronized要比volatile消耗更多资源。
### ### volatilesynchronized 的工作原理对比 在 Java 并发编程中,`volatile` `synchronized` 是实现线程同步的两种关键机制,它们在底层原理使用场景上存在显著差异。 #### 可见性与有序性的实现 `volatile` 关键字通过内存屏障缓存一致性协议来确保变量的可见性有序性。当一个变量被声明为 `volatile` 时,任何对该变量的写操作都会立即刷新到主内存中,并且读取操作会直接从主内存中获取最新值。这种机制保证了多线程环境下变量的可见性[^4]。此外,`volatile` 还能防止指令排序,从而避免由于编译器优化导致的顺序问题。 相比之下,`synchronized` 通过对对象加锁的方式实现了更全面的同步机制。进入同步代码块之前,线程必须获得对象锁;释放锁之后,所有对共享变量的修改才会被写回主内存。这一过程不仅保证了可见性,还通过锁定机制排除了多个线程同时执行临界区的可能性,从而确保了原子性[^4]。 #### 原子性保障 尽管 `volatile` 提供了最低限度的原子操作(如读取写入),但它无法保证复合操作的原子性。例如,自增操作 `i++` 涉及读取、递增写入三个步骤,在没有额外同步措施的情况下,即使是 `volatile` 变量也无法避免竞态条件。因此,对于需要原子性的操作,推荐使用 `java.util.concurrent.atomic` 包中的原子类或通过 `synchronized` 来实现[^2]。 `synchronized` 则天然支持原子性。一旦某个线程获得了对象锁并进入同步代码块,其他尝试获取同一锁的线程将被阻塞,直到当前线程完成操作并释放锁。这种方式有效地隔离了并发访问带来的数据不一致风险,适用于复杂的同步需求。 #### 性能开销 作为轻量级同步工具,`volatile` 的性能通常优于 `synchronized`。它不会引起线程上下文切换,也不涉及锁的获取与释放,因此适合用于状态标记等简单场景。然而,由于其功能有限,过度依赖 `volatile` 容易引发程序错误,尤其是在处理复杂逻辑时[^1]。 `synchronized` 虽然曾经被认为是一种量级锁,但随着 JVM 的不断优化(如偏向锁、轻量级锁等技术的应用),它的性能已经得到了显著提升。特别是在 JDK 6 及以后版本中,`synchronized` 在许多情况下表现出接近甚至超过 `volatile` 的效率,同时提供了更强的安全保障[^2]。 #### 使用建议 鉴于 `volatile` 对技术要求较高且容易出错,除非有充分把握掌握其正确用法,否则应优先考虑使用 `synchronized` 或者 `Lock` 接口提供的锁机制来确保并发安全。尤其在需要保证原子性更复杂同步控制的情形下,这些高级同步工具更为可靠。 ```java // 示例:使用 volatile 实现简单的状态通知机制 public class VolatileExample { private static volatile boolean flag = false; public static void main(String[] args) { new Thread(() -> { while (!flag) { // 等待 flag 变为 true } System.out.println("Flag is now true"); }).start(); try { Thread.sleep(1000); // 模拟延迟 } catch (InterruptedException e) { e.printStackTrace(); } flag = true; // 主线程改变 flag 值 } } ``` ```java // 示例:使用 synchronized 实现计数器 public class SynchronizedCounter { private int count = 0; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值