[java] 스레드 안전하지 않은 코드의 문제점

스레드 안전하지 않은 코드는 여러 스레드가 동시에 접근할 때 예측할 수 없는 결과를 초래하는 문제가 발생할 수 있습니다. 스레드 안전성은 다중 스레드 환경에서 안정적으로 동작하는 것을 의미합니다. 이러한 문제점들을 해결하기 위해 스레드 안전한 코드를 작성해야 합니다.

1. 경쟁 상태 (Race Condition)

경쟁 상태는 여러 스레드가 공유된 자원에 동시에 접근할 때 발생하는 문제입니다. 서로 다른 스레드들이 동시에 값을 읽거나 쓰는 경우, 예상치 못한 결과를 초래할 수 있습니다. 이는 데이터 일관성 문제를 야기할 수 있고, 잘못된 결과 또는 오류를 발생시킬 수 있습니다.

예시 코드

public class Counter {
    private int count = 0;

    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

위의 코드에서 increment() 메서드는 동시에 실행될 경우, count 변수가 잘못된 값을 가질 수 있습니다.

2. 교착 상태 (Deadlock)

교착 상태는 두 개 이상의 스레드가 서로 상대방이 소유한 자원을 대기하고 있는 상태입니다. 이 경우, 스레드들은 서로 자원을 반납할 때까지 무한히 대기 상태에 빠질 수 있습니다. 이로 인해 프로그램은 정지되거나 더 이상 진행되지 않을 수 있습니다.

예시 코드

public class DeadlockExample {
    private static Object resource1 = new Object();
    private static Object resource2 = new Object();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("Thread 1 acquired resource 1");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (resource2) {
                    System.out.println("Thread 1 acquired resource 2");
                }
            }
        });
        
        Thread thread2 = new Thread(() -> {
            synchronized (resource2) {
                System.out.println("Thread 2 acquired resource 2");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (resource1) {
                    System.out.println("Thread 2 acquired resource 1");
                }
            }
        });
        
        thread1.start();
        thread2.start();
    }
}

위의 코드에서 thread1resource1을, thread2resource2를 먼저 얻으려고 합니다. 그러나 두 스레드가 서로의 자원을 기다리면서 프로그램은 아무런 작업을 수행하지 못하고 정지될 수 있습니다.

3. 메모리 일관성 문제

다중 스레드 환경에서 변수에 대한 캐시 일관성 문제가 발생할 수 있습니다. 한 스레드가 변수를 변경하더라도 다른 스레드는 이를 인지하지 못하는 경우가 있을 수 있습니다. 이는 오류를 발생시킬 수 있으며, 예상치 못한 결과를 초래할 수 있습니다.

예시 코드

public class VisibilityProblem {
    private boolean flag = false;

    public void start() {
        new Thread(() -> {
            while (!flag) {
            }
            System.out.println("Flag is now true");
        }).start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        flag = true;
        System.out.println("Flag has been set to true");
    }
}

위의 코드에서 flag 변수는 한 스레드에서 변경되지만, 다른 스레드는 이를 인지하지 못하고 무한히 반복합니다. 이로 인해 Flag is now true라는 출력문이 출력되지 않을 수 있습니다.

스레드 안전성 문제는 복잡한 문제일 수 있으며, 적절한 동기화 기법을 사용하여 이러한 문제들을 해결해야 합니다. Java에서는 synchronized 키워드, Lock 인터페이스와 같은 동기화 메커니즘을 제공하여 스레드 안전한 코드를 작성할 수 있습니다.


참고 자료: