주의 사항 : 힙 영역에 있는 데이터는 모든 스레드에서 접근하여 사용할 수 있다. 이 때 가장 큰 문제점으로는 원자적 작업이 아닌 경우 개발자가 생각한 결과가 안 나올 수 있다. 이처럼 공유되는 영역에 접근하고 thread-safe하지 않는다면 동기화를 진행해줘야 한다.

 

해결법 

  • synchronized 키워드 사용
    • 이 키워드를 통해 여러 스레드가 코드 블록이나 메소드에 동시에 액세스 할 수 없도록 해주는 락킹 메커니즘이다.
    • 소괄호 안에 지정한 객체를 잠금 키로서 사용하는 것 같다. 한 스레드가 접근했으면 객체의 어떤 상태가 1로 변하는 느낌(gpt 답변 인증 받음)
/*
첫 번째 방법
이 키워드는 객체마다 각각 적용되기 때문에 객체 안에 synchronized 메소드가 2개 있고,
threadA가 그 중 한 메소드를 호출했을 때 threadB는 두 synchronized 메소드를 호출 할 수 없다.
이 키워드가 없는 메소드는 동시에 호출할 수 있다.
*/
private static class InventoryCounter {
    private int items = 0;

    public synchronized void increment() {
        items++;
    }

    public synchronized void decrement() {
        items--;
    }

    public int getItems() {
        return items;
    }
}
/* 
두 번째 방법 (추천)
블록만을 동기화하여 메소드의 나머지 부분은 동시에 수행할 수 있도록 할 수 있다. -> 성능 최적화
lock에 대해 여러 개가 있을 경우 메소드 별로 락이 다를 경우 한 메소드에서는 threadA가(threadB, C 접근 X)
다른 메소드에서는 threadB(threadA, C 접근 X)와 같이 유연성을 높일 수 있다.
결국 위의 첫 번째 방법은 아래와 같은 것이다.
public void increment() {
    synchronized (this) {
        items++;
    }
}
따라서 같은 객체에 대해 락을 걸기 때문에 다른 synchronized 메소드가 안 됐던 것이다.
*/
private static class InventoryCounter {
    private int items = 0;
    //락용 객체 생성
    private final Object lock = new Object();

    public void increment() {
        synchronized (this.lock) {
            items++;
        }
    }

    public void decrement() {
        synchronized (this.lock) {
            items--;
        }
    }

    public int getItems() {
        return items;
    }
}

여기서는 위의 첫번째 방법과 같은 형식이다. 다르게 하려면 lock용 객체를 하나 더 만들어야 됨.

 

Tip) synchronized() 인자에는 객체마다 다르게 설정할 수 있으므로 특정 부분 별로 동시성을 따로 챙겨줄 수 있다. 예를 들어 userId 별로 2개 이상 존재하면 안 되는 것이 있을 때는 Map 자료 구조에 userId 별 Object 객체를 생성해주고 이를 이용해서 락을 걸어주면 된다.

'JAVA > 멀티스레딩' 카테고리의 다른 글

락킹 심화  (0) 2024.08.29
병행성 문제와 솔루션  (0) 2024.08.29
Thread Pooling  (0) 2024.08.28
스레드 조정  (0) 2024.08.28
스레드 생성  (0) 2024.08.27

+ Recent posts