[java] 스레드 간의 경쟁 상태(Race Condition)란?

경쟁 상태(Race Condition)는 멀티스레드 환경에서 동시에 여러 스레드가 공유 자원에 접근하여 예상치 못한 결과를 발생시키는 현상을 말합니다. 스레드는 동시에 실행되므로, 각각의 스레드는 동시에 공유 자원에 접근하여 값을 변경하거나 읽을 수 있습니다. 이때 스레드들이 서로 경쟁하면서 값을 변경하려는 경우 예상하지 못한 결과가 발생할 수 있습니다.

경쟁 상태는 일반적으로 공유 자원에 대한 읽기와 쓰기 작업이 동시에 발생하거나, 읽기와 쓰기 사이에 다른 스레드의 작업이 끼어들 때 발생할 수 있습니다. 이러한 작업들이 동시에 발생하면, 예상치 못한 순서로 작업이 수행될 수 있으며, 그에 따라 잘못된 결과가 발생할 수 있습니다.

스레드 간의 경쟁 상태를 방지하기 위해서는 스레드 간의 동기화를 필요로 합니다. 동기화를 통해 스레드 간의 작업 실행 순서를 조정하고, 공유 자원에 대한 접근을 제어함으로써 경쟁 상태를 방지할 수 있습니다. 자바에서는 synchronized 키워드나 Lock/Condition을 이용하여 동기화를 구현할 수 있습니다.

경쟁 상태가 발생하는 코드의 예시는 다음과 같습니다.

public class Main {
    private static int count = 0;

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                count++; // 공유 자원에 접근하여 값을 증가시킴
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                count--; // 공유 자원에 접근하여 값을 감소시킴
            }
        });

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

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Count: " + count);
    }
}

위의 예시에서는 두 개의 스레드가 count 변수를 동시에 접근하여 값을 증가시키거나 감소시킵니다. 이때 스레드 간의 작업 실행 순서는 보장되지 않으므로, 예상치 못한 결과가 발생할 수 있습니다.

경쟁 상태를 해결하려면 synchronized 키워드를 사용하여 메소드 또는 코드 블록을 잠금 상태로 만들어 동기화를 수행할 수 있습니다. 아래는 count 변수의 동기화된 해결책 예시입니다.

public class Main {
    private static int count = 0;

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                synchronized (Main.class) {
                    count++; // 공유 자원에 접근하여 값을 증가시킴
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                synchronized (Main.class) {
                    count--; // 공유 자원에 접근하여 값을 감소시킴
                }
            }
        });

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

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Count: " + count);
    }
}

위의 예시에서는 synchronized 키워드를 이용하여 count 변수에 접근하는 부분을 잠금 상태로 만들었습니다. 이렇게 하면 한 번에 하나의 스레드만 잠금을 획득할 수 있으므로 경쟁 상태를 방지할 수 있습니다.

더 자세한 내용은 아래의 참고 자료를 참고하시기 바랍니다.