CAS英文全(Compare And Swap)指比较并交换。
CAS算法包含3个参数(V,E,N)
- V表示要更新的变量
- E表示预期的值
- N表示新值
在且仅在V值等于E值时,才会将V值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,当前线程什么都不做。最后,CAS返回当前V的真实值。
CAS操作采用了乐观锁的思想,总是认为自己可以完成操作。
在 Java 中,CAS(Compare And Swap)具有以下特性:
- 原子性:CAS 操作是原子性的,意味着它们在多线程环境下是不可分割的。在执行 CAS 操作期间,其他线程无法访问被操作的共享变量。
- 可见性:CAS 操作保证了对共享变量的可见性。当一个线程成功执行了 CAS 操作并更新了共享变量的值时,其他线程会立即看到这个更新。
- 非阻塞性:CAS 操作通常是非阻塞的。如果当前线程的 CAS 操作失败(即预期值与当前值不相等),它不会被阻塞,而是可以立即再次尝试执行 CAS 操作。
- 基于值的比较:CAS 操作基于对共享变量的当前值与预期值进行比较。只有当当前值与预期值相等时,才会进行值的交换。
- 实现乐观锁:CAS 可以用于实现乐观锁的机制。通过在更新数据时使用 CAS 操作,可以避免使用传统的锁机制,从而提高性能。
需要注意的是,CAS 操作存在一些限制和问题,例如 ABA 问题(即一个值经过多次 CAS 操作后可能变回原来的值)。在使用 CAS 时,需要考虑这些限制并采取适当的措施来处理。
总的来说,CAS 是一种用于实现线程安全和并发控制的有用工具,但在使用时需要谨慎处理其特性和限制。
Java 中的 CAS(Compare And Swap)有以下优点:
- 无需使用锁:CAS 操作可以在不使用锁的情况下实现线程安全,避免了锁带来的性能开销。
- 性能高效:CAS 操作是原子性的,保证了并发环境下的正确性,且通常比使用锁的方式更高效。
- 支持非阻塞操作:CAS 操作可以是非阻塞的,线程在执行 CAS 操作时如果失败,可以立即尝试再次执行,而不需要等待锁的释放。
- 简化并发编程:CAS 机制使得并发编程更加简单和容易理解,减少了线程同步和竞争条件的处理。
然而,CAS 也存在一些缺点:
- ABA 问题:CAS 操作可能会遇到 ABA 问题,即一个值经过多次 CAS 操作后可能变回原来的值,导致意外的结果。需要在使用 CAS 时注意处理 ABA 问题。
- 不能保证顺序:CAS 操作不保证操作的顺序性,即多个线程可能以不同的顺序执行 CAS 操作。
- 开销:尽管 CAS 比使用锁更高效,但仍然会有一些开销,包括比较和交换操作本身的开销。
- 范围有限:CAS 通常适用于单个变量的操作,对于复杂的数据结构或多个变量的操作,可能需要使用其他的同步机制。
总体而言,CAS 是一种在 Java 中用于实现线程安全的有用工具,但在使用时需要注意其优缺点,并根据具体情况选择合适的同步机制。
对CAS算法的实现有一个重要的前提:需要取出内存中某时刻的数据,然后在下一时刻进行比较、替换,在这个时间差内可能数据已经发生了变化,导致产生ABA问题。
ABA问题指第1个线程从内存的V位置取出A,这时第2个线程也从内存中取出A,并将V位置的数据首先修改为B,接着又将V位置的数据修改为A,这时第1个线程在进行CAS操作时会发现在内存中仍然是A,然后第1个线程操作成功。尽管从第1个线程的角度来说,CAS操作是成功的,但在该过程中其实V位置的数据发生了变化,只是第1个线程没有感知到罢了,这在某些应用场景下可能会出现过程数据不一致的问题。
部分乐观锁是通过版本号来解决ABA问题的。具体的操作是乐观锁每次在执行数据的修改操作时都会带上一个版本号,在预期的版本号和数据的版本号一致时就可以执行修改操作了,并对版本号的执行加1操作,否则执行失败。因为每次操作的版本号都会随之增加,所以不会出现ABA问题,因为版本号只会增加,不会减少。