>Java >java지도 시간 >CAS 및 Java 낙관적 잠금을 사용하는 방법

CAS 및 Java 낙관적 잠금을 사용하는 방법

王林
王林앞으로
2023-05-01 20:07:161118검색

CAS란

CAS는 CompareAndSwap으로 비교, 교환을 의미합니다. CAS는 왜 잠금을 사용하지 않지만 동시에 발생하는 조건에서 안전한 데이터 조작을 보장합니까? 실제로 CAS의 원리를 매우 직관적으로 보여줍니다. 구체적인 데이터 수정 프로세스는 다음과 같습니다.

  1. CAS를 사용하여 데이터를 조작할 때 원래 값 의 데이터와 수정할 값도 메소드에 전달됩니다

  2. 현재 대상 변수 값이 전달된 원래 값과 동일한지 비교

  3. 동일하면 대상이라는 의미입니다 변수가 다른 스레드에 의해 수정되지 않았으니 직접 타겟 변수 값을 수정하세요

  4. 타겟 변수 값이 원래 값과 다르다면, 이는 타겟 변수가 다른 스레드에 의해 수정되었으며 이번 CAS 수정이 실패했음을 증명합니다

위 과정을 보면 CAS는 실제로 데이터의 안전한 수정을 보장하지만 수정이 실패할 가능성이 있습니다. 즉, 이때 대상 변수 데이터 수정이 실패하게 됩니다. CAS가 데이터를 수정한 결과를 확인하고, 실패하면 다시 시도합니다.

좀 더 신중하게 생각하는 학생들은 CAS 자체의 비교 및 ​​교체 작업으로 인해 동시성 보안 문제가 발생할 수 있다고 걱정할 수도 있습니다. 실제 응용 프로그램에서는 이러한 상황이 하드웨어 수준 CAS의 도움으로 JDK에서 수행됩니다. 비교와 대체가 원자적 작업임을 보장하는 기본 요소입니다.

CAS는 잠금 없는 프로그래밍을 구현합니다

잠금 없는 프로그래밍은 잠금을 사용하지 않고 공유 변수가 안전하게 동작하는 것을 말합니다병행성 프로그래밍에서는 공유 변수의 안전성을 보장하기 위해 다양한 잠금을 사용합니다. 즉, 한 스레드가 공유 변수 작업을 완료하지 않은 경우 다른 스레드가 동일한 공유 변수를 작업할 수 없다는 것이 보장됩니다.
잠금을 올바르게 사용하면 동시성 하에서 데이터 보안을 보장할 수 있지만, 동시성 수준이 높지 않고 경쟁이 치열하지 않은 경우 잠금을 획득하고 해제하는 것은 불필요한 성능 낭비가 됩니다. 이 경우 데이터 보안을 보장하고 잠금 없는 프로그래밍을 달성하기 위해 CAS 사용을 고려할 수 있습니다.

문제가 되는 ABA 문제

공유 변수의 안전한 작동을 보장하기 위한 CAS의 원리를 이미 이해했지만 위의 CAS 작동은 여전히 ​​​​여전합니다. 결함이 있습니다. 현재 스레드가 접근하는 공유변수의 값이 A라고 가정하자. 스레드 1이 공유변수에 접근하는 과정에서 스레드 2는 공유변수를 연산하여 B에 할당한다. 스레드 2는 자신의 로직을 처리한 후 A에 대한 공유변수 이때 스레드 1은 공유변수 값 A와 원래 값 A를 비교하여 다른 스레드가 해당 공유변수를 조작하지 않는다고 잘못 믿고 바로 조작 성공을 반환합니다. 이것이 ABA 문제입니다. 대부분의 기업에서는 공유 변수에 다른 변경 사항이 있는지 신경 쓸 필요가 없지만 원래 값이 현재 값과 일치하는 한 올바른 결과를 얻을 수 있습니다. 공유 변수의 결과는 수정되지 않는 것과 동일하지만 프로세스의 다른 스레드에서 공유 변수를 수정하는 것도 허용되지 않습니다. 다행히 ABA 문제에 대한 성숙한 해결책이 있습니다. 공유 변수에 버전 번호를 추가하면 공유 변수가 수정될 때마다 버전 번호 값이 자동으로 증가합니다. CAS 연산에서 우리가 비교하는 것은 원래 변수 값이 아니라 공유 변수의 버전 번호입니다. 각 공유 변수 작업에 대해 업데이트되는 버전 번호는 고유하므로 ABA 문제를 피할 수 있습니다.

특정 애플리케이션 시나리오

JDK의 CAS 애플리케이션

우선, 여러 스레드가 일반 변수에 대해 동시 작업을 수행하는 것은 안전하지 않습니다. 예를 들어, 이제 우리는 다음을 사용합니다. 두 개의 스레드, 각 스레드는 초기값 1로 공유 변수를 하나씩 증가시킵니다. 동기화 메커니즘이 없으면 공유 변수의 결과는 3보다 작을 가능성이 높습니다. 즉, 스레드 1과 스레드 2가 모두 초기 값 1을 읽었고 스레드 1이 이를 2에 할당했으며 스레드 2가 위치한 메모리에서 읽은 값은 변경되지 않고 그대로 남아 있을 수 있습니다. 1을 2로 할당하여 2에 할당하므로 최종 결과는 예상 결과 3보다 작은 2입니다. 증분 연산은 원자 연산이 아니므로 공유 변수 연산의 안전하지 않은 문제가 발생합니다. 이 문제를 해결하기 위해 JDK는 해당 원자 작업을 제공하는 일련의 원자 클래스를 제공합니다. 다음은 AtomicInteger의 getAndIncrement 메소드의 소스 코드입니다. CAS를 사용하여 정수 변수의 스레드 안전 원자 추가를 구현하는 방법을 살펴보겠습니다.

아아아아

getAndIncrement가 실제로 UnSafe 클래스의 getAndAddInt 메소드를 호출하여 원자적 연산을 구현하는 것을 볼 수 있습니다. 다음은 getAndAddInt 소스 코드

<code>/**<br> * 原子性的将当前值增加1<br> *<br> * @return 返回自增前的值<br> */<br>public final int getAndIncrement() {<br>    return unsafe.getAndAddInt(this, valueOffset, 1);<br>}<br></code>
입니다.

우리 모두는 재진입 잠금 ReentrantLock과 같은 잠금에 익숙합니다. JDK에서 제공하는 다양한 잠금은 기본적으로 AbstractQueuedSynchronizer 클래스에 의존합니다. 여러 스레드가 잠금을 획득하려고 하면 대기열에 들어가 대기하게 됩니다. 스레드 인큐 작업은 CAS에서 안정성을 보장합니다. 소스 코드는 다음과 같습니다.

<code>/**<br> * 原子的将给定值与目标字变量相加并重新赋值给目标变量<br> *<br> * @param o 要更新的变量所在的对象<br> * @param offset 变量字段的内存偏移值<br> * @param delta 要增加的数字值<br> * @return 更改前的原始值<br> * @since 1.8<br> */<br>public final int getAndAddInt(Object o, long offset, int delta) {<br>    int v;<br>    do {<br>    	// 获取当前目标目标变量值<br>        v = getIntVolatile(o, offset);<br>    // 这句代码是关键, 自旋保证相加操作一定成功<br>    // 如果不成功继续运行上一句代码, 获取被其他<br>    // 线程抢先修改的变量值, 在新值基础上尝试相加<br>    // 操作, 保证了相加操作的原子性<br>    } while (!compareAndSwapInt(o, offset, v, v + delta));<br>    return v;<br>}<br></code>

엔터프라이즈 개발의 낙관적 잠금 응용

JDK의 Uusafe 클래스에서 제공하는 다양한 원자 작업 외에도 다음을 사용할 수 있습니다. 실제 개발의 동시성을 보장하는 CAS 아이디어 데이터베이스를 안전하게 운영합니다. 사용자 테이블 구조와 데이터가 다음과 같다고 가정합니다. 버전 필드는 낙관적 잠금을 구현하는 핵심입니다. 샤오밍

0 0

假设我们有一个用户领取优惠券的按钮,怎么防止用户快速点击按钮造成重复领取优惠券的情况呢。我们要安全的更改id为1的用户的coupon_num优惠券数量,将version字段作为CAS比较的版本号,即可避免重复增加优惠券数量,比较和替换这个逻辑通过WHERE条件来实现. 涉及sql如下:

<code>UPDATE user <br>SET coupon_num = coupon_num + 1, version = version + 1 <br>WHERE version = 0</code>

可以看到,我们查询出id为1的数据, 版本号为0,修改数据的同时把当前版本号当做条件即可实现安全修改,如果修改失败,证明已经被其他线程修改过,然后看具体业务决定是否需要自旋尝试再次修改。这里要注意考虑竞争激烈的情况下多个线程自旋导致过度的性能消耗,根据并发量选择适合自己业务的方式

위 내용은 CAS 및 Java 낙관적 잠금을 사용하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제