레이스 컨디션

squareBird·2022년 8월 12일
0

Java

목록 보기
5/5

Java에서 애플리케이션 개발을 진행하다 보면 Multi Thread를 활용한 프로그래밍을 경험하게 된다.

이로인해 Single Thread로 개발을 진행할 경우에는 발생하지 않았던 몇몇 문제점들이 발생하는데 그 중 하나가 RaceCondtiion이다.

RaceCondition이란 공유 자원에 대해 여러개의 Thread 또는 Process가 동시에 접근하기 위해 경쟁하는 상태를 의미한다.


Multi Thread

Multi Thread는 말 그대로 하나의 Process에서 Thread를 여러개 사용하는 것을 말한다.

이 때, Process 내에 존재하는 Thread는 메모리의 Heap영역과 Data영역을 공유하게 된다.

이 메모리를 공유하는 것은 적절하게 사용한다면 Context Switching에 사용되는 비용을 최소화해 애플리케이션의 성능을 향상시킬 수 있다.

하지만, 부적절하게 사용한다면 여러 Thread들이 공유된 특정 자원에 동시에 접근할 경우 문제가 발생할 수 있는데, 이 때 발생할 수 있는 문제점 중 하나가 Race Condition이다.


Race Condition

예를 들어 아래와 같은 코드가 있다고 해보자.

int a = 1;
a = a + 1;

이 때, 프로그램이 동작하는 순서는 다음과 같다.

  1. a를 메모리에 올린다.
  2. a에 1을 더한다.
  3. 결과를 저장한다.

만약, 이 프로그램이 Single Thread에서 동작한다면 이 프로그램에 큰 문제는 없다.
그런데, 만약 이 프로그램이 Multi Thread에서 동작하고 있다면 문제가 발생할 수 있다.

만약 2개의 Thread가 존재하고 2개의 Thread 모두 a에 1을 더하는 동작을 수행한다고 생각해보자.

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

그렇기 때문에 위와 같은 결과가 나올 수 있다.

Thread1에서 a를 메모리에 올리고 1을 더하는 작업이 수행 되었지만, Context Switching이 발생하여 Thread2가 동작하면서 Thread1a에 값을 저장하지 않았으므로 다시 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을 해결하기 위한 방법은 어떤것이 있을까?


1. Mutex(뮤텍스)

첫 번째 방법은 Mutex이다.

Mutex를 기반으로 한 상호 배제에서는 Key를 통해 리소스로의 접근을 관리한다.

Thread가 공유된 리소스에 접근하기 위해서는 Key를 획득해야 하며, Key를 가지고 있지 않은 Thread는 다른 Thread가 리소스를 사용한 뒤 Key를 반납하기를 기다려야 한다.

이 때, 이 Key는 동기화(Synchronization)또는 락(Lock)을 통해 구현한다.

2. Semaphore(세마포어)

두 번째 방법은 Semaphore이다.

Semaphore에서는 Key는 존재하지 않지만, 특정 리소스에 대해 접근할 수 있는 Thread의 최대 수를 관리하는 값이 존재한다.

Thread들은 Share Resource영역에 있는 값을 사용하기 전에 먼저 Semaphore의 값을 확인한다. 이 때, Semaphore의 값이 0이면 리소스를 사용할 수 없으며 다른 Thread가 리소스를 사용 완료한 뒤 Semaphore의 값이 증가하여 0이 아니게 되면 리소스를 사용할 수 있다.


Mutex와 Semaphore의 차이점

그렇다면 두 방식의 공통점과 차이점은 무엇일까?

먼저 공통점은 Thread의 독립적 실행 즉 상호 배제를 달성하기 위한 방법이라는 것이다.

차이점은 아래와 같다.

  1. Mutex는 동기화 대상이 오직 1개일 때 사용하며, Semaphore는 동기화 대상이 1개 이상일 때 사용한다.
  2. Mutex는 자원을 소유할 수 있고 책임을 가지며, Semaphore는 자원을 소유할 수 없다.
  3. Mutex는 상태가 0, 1 뿐이므로 Lock을 가질 수 있고 이 Lock을 소유하고 있는 스레드만이 Mutex를 해제할 수 있는 반면, Semaphore는 Semaphore를 소유하지 않은 스레드가 Semaphore를 해제할 수 있다.
  4. Semaphore는 시스템 범위에 걸쳐 있고, 파일 시스템상에 파일로 존재한다. 반면, Mutex는 프로세스의 범위를 가지며 프로세스가 종료될 때 자동으로 Clean up 된다
profile
DevOps...

0개의 댓글