MST

星途 面试题库

面试题:Java中如何保证原子性操作

在Java内存模型下,说明实现原子性操作的几种方式,并简要阐述每种方式的原理及适用场景。
34.7万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

1. 使用 synchronized 关键字

  • 原理synchronized 关键字通过监视器锁(monitor)来实现同步。当一个线程进入同步块时,它会获取监视器锁,其他线程就无法进入该同步块,直到持有锁的线程释放锁。这保证了同一时间只有一个线程能执行同步块中的代码,从而实现原子性操作。
  • 适用场景:适用于对代码块或方法进行同步控制,确保多个线程对共享资源的访问是线程安全的。常用于对临界区代码的保护,例如对共享变量的读写操作。

2. 使用 java.util.concurrent.atomic 包下的原子类

  • 原理:这些原子类利用了硬件级别的原子操作指令(如 CAS - Compare and Swap)。以 AtomicInteger 为例,它通过 Unsafe 类提供的底层操作来实现原子性。CAS 操作包含三个操作数:内存位置(V)、预期原值(A)和新值(B)。只有当内存位置的值与预期原值相匹配时,才会将该位置的值更新为新值,否则不执行任何操作,并返回当前实际值。这种操作是原子的,因为它在硬件层面保证了在执行期间不会被其他线程干扰。
  • 适用场景:适用于对单个变量进行原子性操作的场景,比如计数器、状态标识等。相比 synchronized,原子类在高并发下具有更好的性能,因为它避免了锁竞争带来的开销。

3. 使用 Lock 接口及其实现类(如 ReentrantLock

  • 原理Lock 接口提供了比 synchronized 更灵活的同步控制。ReentrantLock 内部通过 AQS(AbstractQueuedSynchronizer)框架来实现锁的获取与释放。线程获取锁时,如果锁可用,则获取成功并将锁的持有计数加 1;如果锁不可用,则线程会被放入等待队列。当持有锁的线程释放锁时,将持有计数减 1,当计数为 0 时,锁变为可用,等待队列中的线程可以竞争获取锁。
  • 适用场景:适用于需要更细粒度的锁控制,例如需要实现公平锁(ReentrantLock 可以通过构造函数参数设置为公平锁)、可中断的锁获取、锁的条件变量等场景。相比 synchronizedLock 提供了更多的功能,但使用起来也更加复杂。