스핀 잠금 장치는 무엇입니까?
스핀 잠금 장치에 대해 말하면, 우리는 멀티 스레딩 아래의 잠금 메커니즘으로 시작해야합니다. 다중 프로세서 시스템 환경의 일부 리소스는 제한되어 있으므로 때로는 상호 배제가 필요합니다. 현재 잠금 메커니즘이 도입됩니다. 잠금을 얻는 프로세스 만 리소스 액세스를 얻을 수 있습니다. 즉, 하나의 프로세스만이 자체 임계 영역에 들어가기 위해 한 번에 잠금을 획득 할 수 있습니다. 동시에 두 개 이상의 프로세스가 임계 영역에 들어갈 수 없습니다. 임계 영역을 종료 할 때 잠금이 해제됩니다.
Mutex 알고리즘을 설계 할 때는 항상 자물쇠가없는 상황에 직면 해 있습니다. 즉, 자물쇠가 없으면 어떻게해야합니까?
일반적으로 처리하는 두 가지 방법이 있습니다.
하나는 잠금 장치를 얻지 못한 발신자가 스핀 잠금 장치의 홀더가 잠금 장치를 방출했는지 확인하기 위해 루핑을하고 있다는 것입니다. 이것은이 기사의 초점입니다 - 스핀 잠금. 그는 라인 시티 (비 차단)를 차단할 필요가 없습니다.
또 다른 방법은 잠금 블록 자체 (차단)를 얻지 않고 프로세스가 스레드에서 다른 작업을 계속 실행한다는 것입니다.이 작업은 MUTEX (내장 잠금 동기화, 재진입 락 등 포함)입니다.
소개
CAS (비교 및 스왑), 즉 비교 및 교환은 또한 우리가 일반적으로 스핀 잠금 장치 또는 낙관적 잠금이라고 부르는 것을 구현하는 핵심 조작입니다.
구현은 매우 간단하며 예상 값을 메모리 값과 비교하는 것입니다. 두 값이 같으면 메모리 값을 예상 값으로 바꾸고 true를 반환하십시오. 그렇지 않으면 거짓을 반환하십시오.
원자 작동을 보장하십시오
특정 특정 문제를 해결하기 위해 모든 기술이 등장합니다. CAS가 해결 해야하는 문제는 원자 운영을 보장하는 것입니다. 원자 연산이란 무엇입니까? 원자는 가장 작고 음란하며 원자 작용은 가장 작고 음란 한 작업입니다. 즉, 작업이 시작되면 중단 될 수 없으며 작업이 완료되었음을 알고 있습니다. 다중 스레드 환경에서 원자 연산은 스레드 안전을 보장하는 중요한 수단입니다. 예를 들어, 두 개의 스레드가 작동하고 특정 값을 수정하려고한다고 가정합니다. 자체 점수 작업을 예로 들어보십시오. 정수 I에서 자체 증가 작업을 수행하려면 세 가지 기본 단계가 필요합니다.
1. i의 현재 값을 읽으십시오.
2. I 값에 1을 추가하십시오.
3. i 값을 메모리에 다시 쓰십시오.
두 프로세스가 I의 현재 값을 읽고,이 시점에서, 이시기에, 스레드 A는 1에 1을 추가하고, 스레드 B는 또한 1을 추가하고, 마지막으로 1이지만, 2가 아니라 1이기도합니다. 이는 자동 분쟁 작동이 원자 작동이 아니며 3 단계를 간섭 할 수 있기 때문입니다. 아래의 예에서와 같이 10 개의 스레드의 경우 각 스레드는 10,000 I ++ 작업을 수행하고 예상 값은 100,000이지만 불행히도 결과는 항상 100,000보다 작습니다.
정적 int i = 0; public static void add () {i ++; } private static class plus emplements runnable {@override public void run () {for (int k = 0; k <10000; k ++) {add (); }}} public static void main (string [] args)은 InterruptedException {thread [] 스레드 = 새 스레드 [10]; for (int i = 0; i <10; i ++) {스레드 [i] = 새 스레드 (new plus ()); 스레드 [i] .start (); } for (int i = 0; i <10; i ++) {스레드 [i] .join (); } system.out.println (i); }이 경우 어떻게해야합니까? 맞습니다. 아마도 이미 생각했을 수도 있습니다. 예를 들어 Add () 메소드를 다음과 같은 동기화 된 구현을 잠그거나 사용할 수 있습니다.
public synchronized static void add () {i ++; }또는 예를 들어 ReentrantLock (ReintrantLock)을 사용하여 잠금 작업이 구현됩니다.
개인 정적 잠금 잠금 = 새로운 재 렌트 링크 (); public static void add () {lock.lock (); i ++; lock.unlock (); } CAS는 스핀 잠금 장치를 구현합니다
잠금 또는 동기화 된 키워드를 사용하여 원자 운영을 구현할 수 있으므로 CAS를 사용하는 이유는 무엇입니까? 잠금 또는 동기화 된 키워드를 사용하면 성능이 크게 손실되기 때문에 CAS를 사용하면 낙관적 잠금을 달성 할 수 있습니다. 실제로 CPU 수준 지침을 직접 사용하므로 성능이 매우 높습니다.
위에서 언급했듯이 CAS는 스핀 잠금 장치를 구현하기위한 기초입니다. CAS는 CPU 지침을 사용하여 작업의 원자력을 보장하여 잠금 효과를 달성합니다. 스핀에 관해서는, 문자 그대로의 의미를 읽는 것도 매우 분명합니다. 직접 회전하면 루프입니다. 일반적으로 무한 루프를 사용하여 구현됩니다. 이러한 방식으로 CAS 작업은 무한 루프로 실행됩니다. 작업이 성공하고 True가 반환되면 루프가 종료됩니다. False가 있으면 루프가 실행되고 CAS 조작은 True가 반환 될 때까지 계속됩니다.
실제로, JDK의 많은 장소는 CAS를 사용합니다. 특히 CountdownLatch, Semaphore, ReintrantLock 및 Java.util.concurrent.atomic 패키지와 같은 Java.util.concurrent 패키지에서. 나는 모든 사람들이 Atomicboolean, atomicinteger 등과 같은 Atomic*을 사용했다고 생각합니다.
여기서 우리는 충분히 간단하기 때문에 Atomicboolean을 예로 들어갑니다.
공개 클래스 Atomicboolean은 Java.io.serializable을 구현합니다. {private static final long serialversionuid = 4654671469794556979L; // 업데이트를 위해 unsafe.compareAndswapint를 사용하도록 설정합니다. 개인 정적 최종 최종 장거리 밸류러스; static {try {valueOffset = unsafe.objectfieldoffset (atomicboolean.class.getDeclaredfield ( "value")); } catch (Exception Ex) {Throw New Error (예 :); }} 개인 휘발성 int 값; 공개 최종 부울 get () {return value! = 0; } public final boolean compareandset (부울 기대, 부울 업데이트) {int e = expect? 1 : 0; int u = 업데이트? 1 : 0; insafe.compareAndswapint (this, valueOffset, e, u); }}이것은 Atomicboolean 코드의 일부이며 여기에는 몇 가지 주요 방법과 속성이 있습니다.
1. Sun.misc.unsafe 객체가 사용됩니다. 이 클래스는 메모리 객체를 직접 작동시키는 일련의 방법을 제공하지만 JDK에서만 내부적으로 사용되며 개발자가 사용하는 것이 권장되지 않습니다.
2. 값은 실제 값을 나타냅니다. get 메소드는 실제로 값이 0과 같은지에 따라 부울 값을 판단한다는 것을 알 수 있습니다. 여기의 값은 휘발성으로 정의됩니다. 휘발성은 메모리 가시성을 보장 할 수 있기 때문에, 즉 값 값이 변경되는 한 다른 스레드가 변경된 값을 즉시 볼 수 있기 때문입니다. 다음 기사는 휘발성의 가시성에 대해 이야기합니다.
3. ValueOffset은 unsafe.objectfieldoffset 메소드를 사용하여 얻은 값 값의 메모리 오프셋입니다.
4. 비교 에드셋 방법, 이것은 CAS를 구현하는 핵심 방법입니다. AtomicBoolean 메소드를 사용하는 경우 예상 값과 업데이트 할 값 만 전달하면됩니다. unsafe.compareAndswapint (this, valueOffset, e, u) 메소드가 호출됩니다. C ++로 구현 된 기본 방법이며 특정 코드는 게시되지 않습니다. 요컨대, CPU의 CMMPXCHG 명령어를 사용하여 비교 및 교체를 완료합니다. 물론 특정 시스템 버전에 따라 구현에도 차이가 있습니다. 관심있는 사람들은 관련 기사를 스스로 검색 할 수 있습니다.
시나리오를 사용하십시오
예를 들어, Atomicboolean은 이러한 시나리오에서 사용할 수 있습니다. 시스템은 부울 변수의 상태 특성에 따라 일부 초기화 작업을 수행 해야하는지 확인해야합니다. 다중 스레드 환경이고 반복적 인 실행을 피하면 Atomicboolean을 사용하여 구현할 수 있습니다. 의사 코드는 다음과 같습니다.
개인 최종 정적 AtomicBoolean 플래그 = New AtomicBoolean (); if (flag.compareAndset (false, true)) {init (); }예를 들어, Atomicinteger는 카운터 및 다중 스레드 환경에서 사용하여 정확한 계산을 보장 할 수 있습니다.
ABA 질문
CAS에는 값이 A에서 B로, B에서 B로 변경됩니다.이 경우 CAS는 값이 변경되지 않았다고 생각하지만 실제로는 변경되었습니다. 이와 관련하여, 동시 패킷 아래에서 AtomicStampedReference가 있으며 버전 번호를 기반으로 구현을 제공하는 일부 문제를 해결할 수 있습니다.
요약
위는이 기사의 전체 내용입니다. 이 기사의 내용에 모든 사람의 연구 나 작업에 대한 특정 참조 가치가 있기를 바랍니다. 궁금한 점이 있으면 의사 소통을 위해 메시지를 남길 수 있습니다. Wulin.com을 지원 해주셔서 감사합니다.