상황 가정 + 실패 예시

아래 예시는 좋지 않은 예시이다.

만약 두개의 스레드 각 10000번 돌려 두 스레드 값을 더하여 20000이 나오게 하는 프로그램을 작성한다고 가정하자.

public class SyncTest1BadMain {

    public static void main(String[] args) throws InterruptedException {

        Counter counter = new Counter();

        Runnable task = new Runnable(){

            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    counter.increment();
                }
            }
        };

        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);

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

        thread1.join();
        thread2.join();
        System.out.println("결과 : " + counter.getCount());
    }

    static class Counter {
        private int count = 0;

        public void increment() {
            count += 1;
        }

        public int getCount() {
            return count;
        }
    }
}

위 예시 결과는 아래와 같다. (운이 좋아 20000이 나올 수도 있긴 함)

 


문제점 + 해결 로직

최초에 스레드 두개가 동시에 실행 되면, 두 스레드가 공유 자원의 값을 0으로 읽어버린다.

코드의 흐름이 count 값을 읽어서 계산하는 부분 + 결과를 count 변수에 넣는 두가지 로직으로 구분이 되는데, 서로 공유자원의 값을 계속 덮어 쓰게 된다.

이때 각 스레드가 서로 다른 값을 읽으며 결과 값을 누적해간다면 결과 값을 적절히 얻어올 수 없다. -> 이럴 때 synchronized 키워드가 필요하다

 

public class SyncTest1Main {

    public static void main(String[] args) throws InterruptedException {

        Counter counter = new Counter(); // 공유 자원

        Runnable task = new Runnable(){
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    counter.increment();
                }
            }
        };

        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);

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

        thread1.join();
        thread2.join();

        System.out.println("결과 : " + counter.getCount());
    }

    static class Counter {
        private int count = 0;

        public synchronized void increment() {
            count += 1;
        }

        public synchronized int getCount() {
            return count;
        }
    }
}

적절히 싱크로나이즈를 사용한 덕분에 몇번을 돌려도 20000이 나오는 것을 확인 할 수 있었다.