Java synchronized 한정자/블록 - 멀티스레드 환경에서 독점 제어

synchronized

다중 스레드 환경에서 공유 데이터를 조작할 때 처리 충돌에 주의가 필요하다. synchronized 한정자는 멀티스레드 환경에서 독점 제어할 시에 활용된다.

synchronized 사용하시 않은 경우

다음은 인스턴스 변수 count를 여러 스레드에서 동시에 증가시키는 작업이다.

package com.devkuma.basic.thread;

public class SynchronizedSample {
    private int count;
    private static int THREAD_MAX = 300000;

    private static class MyThread implements Runnable {
        private SynchronizedSample _count;

        public MyThread(SynchronizedSample count) {
            this._count = count;
        }

        @Override
        public void run() {
            _count.increment();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynchronizedSample ss = new SynchronizedSample();
        Thread[] ts = new Thread[THREAD_MAX];
        for (int i = 0; i < THREAD_MAX; i++) {
            ts[i] = new Thread(new MyThread(ss));
            ts[i].start();
        }
        for (int i = 0; i < THREAD_MAX; i++) {
            ts[i].join();
        }
        System.out.println(ss.count);   // 결과: 299944(매번 결과가 다르다.)
    }

    public void increment() {
        this.count++;
    }
}

위 코드는 제대로 작동하지 않는다. 위에 this.count++ 연산에서 count 값을 읽은 후 증가하고 재대입되기 전에 다른 스레드가 인터럽트될 수 있기 때문이다.
그러므로 원래 최종 결과 300,000이 되어야 하지만, 299999와 같은 값이 반환 될 수도 있게 된다.

synchronized 메서드

이를 방지하려면 increment 메서드를 synchronized 한정자로 한정하면 된다.

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

이렇게 함으로써 increment 메소드는 여러 thread로부터 동시에 호출되는 일이 없어진다(나중에 호출된 곳에서는 먼저 들어온 처리가 끝날 때까지 대기한다). 그래서 결과는 매번 30만이라는 결과를 얻을 수 있다.

synchronized 블록(block)

위의 코드는 synchronized 블록(block)을 사용하여 다음과 같이 다시 작성할 수도 있다.

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



최종 수정 : 2022-09-24