Java ‐ CAS - dnwls16071/Backend_Summary GitHub Wiki

📚 CAS 연산

  • 락은 특정 자원을 보호하기 위해 쓰레드가 해당 자원에 접근하는 것을 제한한다.
  • 락이 걸려 있는 동안 다른 쓰레드들은 해당 자원에 접근할 수 없고, 락이 해제될 때까지 대기해야 한다.
  • 또한 락 기반 접근에서는 락을 획득하고 해제하는 데 시간이 소요된다.
  • 락을 사용하는 방식은 직관적이나 상대적으로 무거운 방식이라는 것을 알고 있어야 한다.
1. 락이 있는가 확인한다.
2. 락을 획득하고 임계 영역에 들어간다.
3. 작업을 수행한다.
4. 락을 반납한다.
  • 예를 들어, 10,000번의 연산이 있다면 10,000번의 연산 모두 같은 과정을 반복한다.
  • 이런 문제를 해결하기 위해 락을 걸지 않고 원자적이 연산을 수행할 수 있는 방법이 있는데, 이것을 CAS(Compare-And-Swap, Compare-And-Set) 연산이라고 한다.
  • 이 방법은 락을 사용하지 않기 때문에 락 프리(Rock-Free) 알고리즘 기법이라고 한다. 참고로 CAS 연산은 락을 완전히 대체하는 것은 아니고 작은 단위의 일부 영역에 적용할 수 있다.
  • 기본은 락을 사용하고 특별한 경우에 CAS를 적용할 수 있다고 생각하면 된다.

실행 순서 분석

  • CAS 연산은 원자적이지 않은 2개의 연산을 CPU 하드웨어 차원에서 특별하게 하나의 원자적인 연산으로 묶어서 제공하는 기능이다.
  • 이것은 소프트웨어가 제공하는 기능이 아니라 하드웨어가 제공하는 기능이다.
  • 대부분의 현대 CPU들은 CAS 연산을 위한 명령어를 제공한다.

정리

  • CAS를 사용하면 락을 사용하지 않지만, 대신에 다른 쓰레드가 값을 먼저 증가해서 문제가 발생하는 경우 루프를 돌며 재시도를 하는 방식을 사용한다.
1. 현재 변수의 값을 읽어온다.
2. 변수의 값을 1 증가시킨다.
  1. 원래 값이 같은지 확인한다.(CAS 연산 사용)
3. 동일하다면 증가된 값을 변수에 저장하고 종료한다.
4. 동일하지 않다면 다른 쓰레드가 값을 변경한 것이므로 다시 처음으로 돌아가 위 과정을 반복한다.
  • 두 쓰레드가 동시에 실행되면서 문제가 발생하는 상황을 쓰레드가 충돌했다고 표현한다.
  • 이 과정에서 충돌이 발생할 때마다 반복해서 다시 시도하므로, 결과적으로 락 없이 데이터를 안전하게 변경할 수 있다.
  • CAS를 사용하는 방식은 충돌이 드물게 발생하는 환경에서는 락을 사용하지 않으므로 높은 성능을 발휘할 수 있다.
  • 이는 락을 사용하는 방식과 비교했을 때 쓰레드가 락을 획득하기 위해 대기하지 않기 때문에 대기 시간과 오버헤드가 줄어드는 장점이 있다.
  • 그러나 충돌이 빈번히 발생하는 환경에서는 성능에 문제가 될 수 있다. 여러 쓰레드가 자주 동시에 동일한 변수의 값을 변경하려고 시도할 때 CAS는 자주 실패하고 재시도해야 하므로 성능 저하가 발생할 수 있다. 이런 상황에서 반복문을 계속 돌기 때문에 CPU 자원을 많이 소모하게 된다.

CAS와 Lock의 비교

  • Lock
    • 비관적 접근 방식
    • 데이터에 접근하기 전에 항상 락을 획득
    • 다른 쓰레드의 접근을 막음
    • 다른 쓰레드가 방해할 것이라는 가정을 전제
  • CAS
    • 낙관적 접근법
    • 락을 사용하지 않고 데이터에 바로 접근
    • 충돌이 발생하면 그 때마다 재시도
    • 대부분의 경우 충돌이 없을 것이라는 가정을 전제

📚 CAS 락

  • 원자적 연산은 쓰레드 입장에서 쪼갤 수 없는 하나의 연산이다.
  • 따라서 여러 쓰레드가 동시에 실행해도 안전하다.
  • 동기화 락을 사용할 경우 RUNNABLE 상태의 쓰레드가 BLOCKED, WAITING 상태에서 다시 RUNNABLE 상태로 이동한다. 이 사이에 CPU 자원을 거의 사용하지 않을 수 있다.
  • 그래서 동기화 락을 사용하는 방식보다 쓰레드를 RUNNABLE로 살려둔 상태에서 계속 락 획득을 반복 체크하는 것이 더 효율적인 경우 이런 방식을 사용해야 한다.
  • 이 방식은 쓰레드 상태가 변경되지 않기 때문에 매우 빠르게 락을 획득할 수 있고 또 바로 실행할 수 있다는 장점이 있다.
  • 안전한 임계 영역이 필요하지만 연산이 길지 않고 매우 짧게 끝날 때 사용해야 한다.

Spin Lock(스핀 락)

  • 쓰레드가 락이 해제되기를 기다리면서 반복문을 통해 계속해서 확인하는 모습이 마치 제자리에서 회전하는 것처럼 보인다고 해서 스핀 락이라고 부른다.
  • 그리고 이런 방식에서 쓰레드가 락을 획득할 때까지 대기하는 것을 스핀 대기 또는 CPU 자원을 계속 사용하면서 바쁘게 대기한다고 해서 바쁜 대기라고 한다.
  • 이런 스핀 락 방식은 아주 짧은 CPU 연산을 수행할 때 사용해야 효율적이다. 잘못 사용하게 되면 오히려 CPU 자원을 더 많이 사용할 수 있다.