반응형
개요
- 김영한 강사님의 강의를 듣고 정리하였다.
본론
임계 영역이란?
임계 영역(critical section)이란 여러 스레드가 동시에 접근하게 된다면 데이터의 불일치나 우리가 예상하지 못한 결과가 발생할 수 있는 위험하고 중요한 코드 부분을 뜻한다.
- 여러 스레드들이 동시에 접근해서는 안되는 공유 자원을 접근(조회)하거나 수정하는 부분을 의미한다.
출금 예제
@Override
public boolean withdraw(int amount) {
log("거래 시작 : " + getClass().getSimpleName());
if(balance < amount){
// 실패
return false;
}
sleep(1000); // 출금 시간
balance -= amount;
log("거래 종료");
return true;
}
- 위의 예제는 계좌에서 돈을 꺼내는 출금 로직이다. 이 코드를 여러 스레드가 동시에 작업을 수행하면 어떻게 될까?
2가지 결과
2개의 스레드가 동시에 1000원이 들어있는 계좌에서 800원씩 출금한다고 가정한다.
따라서 우리가 기대하는 결과는 하나의 스레드는 if()문에서 실패하고, 하나의 스레드는 출금에 성공해서 결과 잔액이 200원이 남는 것을 기대한다.
- 하나의 스레드가 if()을 지나가고 sleep() 상태에서 대기한다. 그 후 두 번째 스레드가 if()문을 통과해버리면서 두 번의 balance -= amount; 가 실행되고 결과적으로 계좌의 잔액은 -600원이 된다.
- 2개의 스레드가 2개의 코어에서 동시에 실행되면서 balance -= amount의 연산에서 두 연산 모두 balance =200원이 되면서 두 번의 800원 출금에 잔액은 200원이 된다.
위의 결과는 우리가 예상하는 결과가 아니다.!
- 이런 결과는 balance라는 공유 자원을 여러 스레드가 동시에 접근할 수 있게 되면서 발생하는 문제 즉, 임계 영역에 여러 스레드가 접근함으로써 발생한 문제이다.
그렇다면 어떻게 해결할 수 있을까?
synchronized 메서드
@Override
public synchronized boolean withdraw(int amount) {
log("거래 시작 : " + getClass().getSimpleName());
if(balance < amount){
// 실패
return false;
}
sleep(1000); // 출금 시간
balance -= amount;
log("거래 종료");
return true;
}
자바에서는 synchronized 메서드를 선언함으로써 해당 메서드에 한 번에 하나의 스레드만 접근할 수 있도록 락을 걸어줄 수 있다.
- 이 락은 인스턴스 내부에 모니터락으로 존재한다.
이렇게 synchronized를 이용하면 메서드를 락을 통해 락을 가진 스레드만 진입하도록 할 수 있다.
따라서 메서드의 내부 임계 영역을 안전하게 만들 수 있다.
- 추가로, 락을 획득하기 위해 대기하는 스레드는 BLOCKED 상태가 된다.
- 또한 락을 획득하는 순서는 환경에따라 바뀌기 때문에 순서대로 획득하지 않는다.
synchronized 블럭
synchronized 메서드로 선언하면 해당 메서드 전체를 안전한 임계 영역으로 만들 수 있지만, 이 영역은 한 번에 하나의 스레드만 진입할 수 있으므로 성능이 떨어진다.
- 따라서 정확한 임계 영역만 synchronized를 통해 실행해야 한다.
@Override
public boolean withdraw(int amount) {
log("거래 시작 : " + getClass().getSimpleName());
synchronized(this){
if(balance < amount){
// 실패
return false;
}
sleep(1000); // 출금 시간
balance -= amount;
}
log("거래 종료");
return true;
}
synchronized는 메서드가 아닌 코드 블럭 단위로 적용할 수 있다.
이를 통해 정말 꼭 필요한 임계 영역만 지정할 수 있다.
- 여기서 this는 현재 인스턴스에 있는 모니터 락을 사용한다는 의미이다.
synchronized는 다음의 문제를 해결해준다.
- Race Condition : 두 개 이상의 스레드가 경쟁적으로 동일한 자원을 수정할 때 발생하는 문제를 해결해준다.
- 데이터 일관성 : 여러 스레드가 동시에 읽고 쓰는 데이터의 일관성을 유지해준다.
synchronized의 단점
- BLOCKED 상태의 스레드는 락이 풀릴 때 까지 대기하는데, 타임아웃도 없고 인터럽트도 발생하지 않아 중지시킬 수 없다.
- synchronized는 스레드의 락 획득에 대한 공정성을 보장할 수 없다. 따라서 하나의 스레드가 오랜 시간동안 락을 획득하지 못하는 경우가 발생할 수 있다.
결론
synchronized에 대해서 알아보았다.
전공 시간에 배운 내용들을 직접 자바에서 보니 좀 더 이해가 잘 된 것 같다.
반응형
'JAVA' 카테고리의 다른 글
[JAVA] ReentrantLock이 뭔가요? (0) | 2024.09.24 |
---|---|
[JAVA] java.util.concurrent.LockSupport 알아보자 (0) | 2024.09.24 |
[JAVA] 자바의 volatile 키워드와 메모리 가시성 (1) | 2024.09.23 |
[JAVA] 스레드의 양보 Thread.yield() (0) | 2024.09.21 |
[JAVA] 대기중인 스레드를 RUNNABLE로 깨우려면 어떻게 해야할까? (0) | 2024.09.21 |