Java volatile和synchronized的区别(新手必看)
volatile 和 synchronized 关键字是 Java 中用于同步多个线程之间操作的两种机制,但它们在使用场景、性能开销、实现原理等多个方面都有显著的区别,具体如下。
1) 保证的范围:
2) 使用场景:
3) 性能开销:
4) 锁机制的提供:
5) 原子性:
6) 实现原理:
在实际编码中,应根据需要同步操作的复杂性和性能要求选择适当的同步机制。对于简单的状态标志访问,使用 volatile 关键字可能是最佳选择。而对于需要多个步骤的事务性操作,使用 synchronized 关键字会更合适。
1) 保证的范围:
- volatile 关键字是一个变量修饰符,它确保该变量的读写操作直接作用于主内存,而不是线程的本地内存。它可以保证单个 volatile 变量的读写操作的内存可见性和部分有序性。
- synchronized 关键字提供了一种锁机制,使用该机制能够控制多个线程对资源的并发访问。它不仅保证了区块内所有变量的读写操作的内存可见性和有序性,还提供了互斥的执行,即一次只有一个线程可以执行 synchronized 同步代码块。
2) 使用场景:
- volatile 关键字适用于那些只由一个线程写入,而由一个或多个线程读取的变量。它是一种轻量级的同步策略,经常用于状态标志或单例模式的实现;
- synchronized 关键字适用于复杂的交互场景,这些场景中需要多个操作作为一个原子块来执行,以保护数据的一致性和完整性。
3) 性能开销:
- volatile 关键字通常比 synchronized 关键字有更低的性能开销,因为它不涉及锁的获取和释放;
- synchronized 关键字会引入线程阻塞和唤醒的额外开销,特别是在高度竞争的场景下,性能的损耗会更明显。
4) 锁机制的提供:
- volatile 关键字不提供任何锁的机制,因此它不能用来实现临界区或等待/通知机制;
- synchronized 关键字提供了锁机制,并且可以用来实现临界区内的操作序列化,以及实现等待/通知机制。
5) 原子性:
- volatile 关键字仅保证单个 volatile 变量读或写的原子性,不保证复合操作(如自增)的原子性;
- synchronized 关键字可以保证其中封装的代码块或方法内所有操作的原子性。
6) 实现原理:
- volatile 关键字通过内存屏障实现同步;
- synchronized 关键字依赖于监视器锁(Monitor Lock)的获取和释放机制,在 JVM 层面通过对象头(Object Header)中的 Monitor 实现。
在实际编码中,应根据需要同步操作的复杂性和性能要求选择适当的同步机制。对于简单的状态标志访问,使用 volatile 关键字可能是最佳选择。而对于需要多个步骤的事务性操作,使用 synchronized 关键字会更合适。