返回

直击 CAS 的核心魅力:揭秘它为何能够代替 synchronized

Android

CAS 与 synchronized:并发编程中的两个关键玩家

在多线程编程的世界里,协调多个线程的访问和修改共享资源至关重要。为了确保数据的完整性和程序的正确性,我们需要可靠的同步机制。在 Java 中,CAS(Compare-and-swap)和 synchronized 是两个最常用的选择。

CAS:无锁并发原语

CAS(Compare-and-swap)是一种无锁并发原语,这意味着它不需要获取和释放锁来完成操作。它背后的思想是,在执行更新之前,先比较内存中变量的当前值与预期值是否一致。如果一致,则执行更新;如果不一致,则放弃更新。

步骤详解:

  1. 读取变量的当前值。
  2. 比较变量的当前值与预期值是否一致。
  3. 如果一致,则执行更新操作;如果不一致,则放弃更新。

示例代码:

public class CASDemo {

    private int counter = 0;

    public void incrementCounter() {
        int expectedValue = counter;
        while (!CAS(expectedValue, expectedValue + 1)) {
            // CAS 操作失败,重试
            expectedValue = counter;
        }
    }

    private boolean CAS(int expectedValue, int newValue) {
        return AtomicInteger.compareAndSet(counter, expectedValue, newValue);
    }
}

synchronized:基于锁的同步机制

synchronized 是 Java 中内置的锁机制,它通过获取和释放锁来保证线程间的同步。当一个线程获取到一个对象的锁之后,其他线程在访问该对象时必须等待,直到该线程释放锁。

实现方式:

  1. 内置锁: 使用 synchronized 修饰方法或代码块,可以在方法或代码块执行期间获取对象的锁。
  2. 显式锁: 使用 ReentrantLock 等显式锁对象,可以在代码中手动获取和释放锁。

示例代码:

public class SynchronizedDemo {

    private int counter = 0;

    public synchronized void incrementCounter() {
        counter++;
    }
}

CAS 与 synchronized 的比较

特性 CAS synchronized
原理 无锁并发原语 基于锁的同步机制
性能 高性能 低性能
适用场景 竞争不激烈的场景 竞争激烈的场景
可重入性 不可重入 可重入
公平性 不公平 公平

为什么 CAS 可以替代 synchronized?

在某些场景下,CAS 可以代替 synchronized,主要原因在于其高性能 。由于 CAS 是无锁并发原语,它不需要获取和释放锁,因此开销很小。而 synchronized 需要获取和释放锁,开销较大。

常见问题解答

  1. CAS 是否适用于所有情况?

    • 不,CAS 适用于竞争不激烈的场景。在竞争激烈的场景下,它可能会导致自旋,降低性能。
  2. synchronized 是否会导致死锁?

    • 是的,synchronized 可能导致死锁,尤其是在多个线程相互持有对方的锁的情况下。
  3. CAS 如何避免死锁?

    • CAS 是无锁的,它通过比较预期值和当前值来更新变量,避免了死锁的发生。
  4. 何时应该使用 CAS?

    • 在竞争不激烈且需要高性能的场景下,例如计数器、标志位等。
  5. 何时应该使用 synchronized?

    • 在竞争激烈且需要可重入性和公平性的场景下,例如共享资源的访问等。

总结

CAS 和 synchronized 都是 Java 并发编程中重要的同步机制。CAS 是无锁并发原语,具有高性能和避免死锁的优点,但适用于竞争不激烈的场景。synchronized 是基于锁的同步机制,具有可重入性和公平性的优点,但性能较低,适用于竞争激烈的场景。在实际应用中,需要根据具体场景选择合适的锁机制。