skarltjr / Memory_Write_Record

나의 모든 학습 기록
0 stars 0 forks source link

Atomic 타입과 CAS #135

Open skarltjr opened 1 year ago

skarltjr commented 1 year ago
synchronized 키워드의 문제점
- synchronized는 결국 락을 사용한다
- 락을 사용한다는것은 임계구역에 대해 동시적인 접근이 불가능한데
- 이때 스레드1이 아닌 다른 스레드2,3,4,5는 blocking 상태
- 이로인해 다른일을 하지 못하게되니 자원 낭비가 발생

atomic 타입

synchronized의 문제를 극복하고자 non-blocking 원자성 보장이 가능
cas 알고리즘을 활용한 동작원리

CAS

compare and swap

즉 
- 인자로 기존 값(Compared Value)과 변경할 값(Exchanged Value)을 전달한다.
- 기존 값(Compared Value)이 현재 메모리가 가지고 있는 값(Destination)과 같다면 변경할 값(Exchanged Value)을 반영하며 true를 반환한다.
- 반대로 기존 값(Compared Value)이 현재 메모리가 가지고 있는 값(Destination)과 다르다면 값을 반영하지 않고 false를 반환한다.
참고 : 
- CAS 알고리즘이란 현재 스레드가 존재하는 CPU의 CacheMemory와 MainMemory에 저장된 값을 비교하여, 일치하는 경우 새로운 값으로 교체하고, 일치하지 않을 경우 기존 교체가 실패되고, 이에 대해 계속 재시도하는 방식입니다.
- CPU가 Main Memory의 자원을 CPU Cache Memory로 가져와 연산을 수행하는 동안 다른 스레드에서 연산이 수행되어 Main Memory의 자원 값이 바뀌었을 경우 기존 연산을 실패처리하고, 새로 바뀐 MainMemory 값으로 재수행하는 방식입니다.

정리하자면

cache memory의 값은 임시값
main memory의 값은 origin이라고 생각해보자

임시값과 origin이 같으면 아 지금 바꿔도 괜찮겠구나!
임시값과 origin이 다르면 어? 지금 고치면 안되겠다, origin값 받아온 후 다시 시도해야겠다.

TEST

public class Main {
    static int count = 0;
    public static void main(String[] args) throws IOException, InterruptedException {
        AtomicInteger atomicCount = new AtomicInteger(0);

        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                count++;
                atomicCount.incrementAndGet();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                count++;
                atomicCount.incrementAndGet();
            }
        });

        thread1.start();
        thread2.start();

        Thread.sleep(5000);
        System.out.println(count + " = count");
        System.out.println(atomicCount.get() + " = atomic count");
    }
}