Java에서 애플리케이션 개발을 진행하다 보면 Multi Thread를 활용한 프로그래밍을 경험하게 된다.
이로인해 Single Thread로 개발을 진행할 경우에는 발생하지 않았던 몇몇 문제점들이 발생하는데 그 중 하나가 RaceCondtiion이다.
RaceCondition이란 공유 자원에 대해 여러개의 Thread 또는 Process가 동시에 접근하기 위해 경쟁하는 상태를 의미한다.
Multi Thread는 말 그대로 하나의 Process에서 Thread를 여러개 사용하는 것을 말한다.
이 때, Process 내에 존재하는 Thread는 메모리의 Heap영역과 Data영역을 공유하게 된다.
이 메모리를 공유하는 것은 적절하게 사용한다면 Context Switching에 사용되는 비용을 최소화해 애플리케이션의 성능을 향상시킬 수 있다.
하지만, 부적절하게 사용한다면 여러 Thread들이 공유된 특정 자원에 동시에 접근할 경우 문제가 발생할 수 있는데, 이 때 발생할 수 있는 문제점 중 하나가 Race Condition이다.
예를 들어 아래와 같은 코드가 있다고 해보자.
int a = 1;
a = a + 1;
이 때, 프로그램이 동작하는 순서는 다음과 같다.
a를 메모리에 올린다.a에 1을 더한다.만약, 이 프로그램이 Single Thread에서 동작한다면 이 프로그램에 큰 문제는 없다.
그런데, 만약 이 프로그램이 Multi Thread에서 동작하고 있다면 문제가 발생할 수 있다.
만약 2개의 Thread가 존재하고 2개의 Thread 모두 a에 1을 더하는 동작을 수행한다고 생각해보자.

위의 표와 같이 동작한다면 우리는 a에 1을 더하는 작업을 두번 하는 코드를 통해 기대하고 있던 3이라는 결과값을 얻을 수 있을것이다. 하지만, Java에서 여러개의 Thread가 동시에 실행될때는 어떤 Thread가 먼저 실행될지 알 수 없다.

그렇기 때문에 위와 같은 결과가 나올 수 있다.
Thread1에서 a를 메모리에 올리고 1을 더하는 작업이 수행 되었지만, Context Switching이 발생하여 Thread2가 동작하면서 Thread1이 a에 값을 저장하지 않았으므로 다시 1을 가져와서 계산을 한 후 2라는 결과값을 저장한다.
그 다음 다시 Context Switching이 발생하면 Thread1은 기존에 a에 1을 저장하는 동작을 수행했으니 다음 동작인 결과를 저장하는 동작을 수행한다.
이 때, Thread1이 가지고 있던 값은 기존 a에 1을 더한 값인 2이므로 다시 2가 저장된다.
우리는 3이라는 결과값을 기대했지만, Context Switching이 발생하면서 2라는 우리가 기대하지 않은 값을 얻게되었으며, 이와 같은 문제를 Race Condition이라고 한다.
Race Condition을 해결하기 위해서는 임계 구역을 가진 Thread들이 서로 겹치지 않고 단독으로 실행 즉 상호 배제(Mutual Exclution)되는 것이 보장되어야 한다.
그렇다면 이 Race Condition을 해결하기 위한 방법은 어떤것이 있을까?

첫 번째 방법은 Mutex이다.
Mutex를 기반으로 한 상호 배제에서는 Key를 통해 리소스로의 접근을 관리한다.
Thread가 공유된 리소스에 접근하기 위해서는 Key를 획득해야 하며, Key를 가지고 있지 않은 Thread는 다른 Thread가 리소스를 사용한 뒤 Key를 반납하기를 기다려야 한다.
이 때, 이 Key는 동기화(Synchronization)또는 락(Lock)을 통해 구현한다.

두 번째 방법은 Semaphore이다.
Semaphore에서는 Key는 존재하지 않지만, 특정 리소스에 대해 접근할 수 있는 Thread의 최대 수를 관리하는 값이 존재한다.
각 Thread들은 Share Resource영역에 있는 값을 사용하기 전에 먼저 Semaphore의 값을 확인한다. 이 때, Semaphore의 값이 0이면 리소스를 사용할 수 없으며 다른 Thread가 리소스를 사용 완료한 뒤 Semaphore의 값이 증가하여 0이 아니게 되면 리소스를 사용할 수 있다.
그렇다면 두 방식의 공통점과 차이점은 무엇일까?
먼저 공통점은 Thread의 독립적 실행 즉 상호 배제를 달성하기 위한 방법이라는 것이다.
차이점은 아래와 같다.