경쟁 상태(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 변수에 접근하는 부분을 잠금 상태로 만들었습니다. 이렇게 하면 한 번에 하나의 스레드만 잠금을 획득할 수 있으므로 경쟁 상태를 방지할 수 있습니다.
더 자세한 내용은 아래의 참고 자료를 참고하시기 바랍니다.
- Oracle Java Documentation: Concurrency
- Baeldung: Java Thread Synchronization