스레드 세이프란?
: 멀티 스레드 환경에서 변수, 객체나 함수 등의 자원이 여러 스레드에 의해 동시에 접근되어도 프로그램이 정상적으로 동작하는 상태를 스레드 세이프하다고 말한다.
- 스레드 세이프하지 않은 경우
- 아래 코드는 싱글 스레드 환경에서는 문제가 없지만, 멀티 스레드 환경에서는 전역 변수를 공유하기 때문에 문제가 발생할 수 있다. (ex. 한 스레드가 연산 중에 또다른 스레드가 result값을 읽어와서 연산하는 경우)
public class NoThreadSafeClass {
private int result = 0;
public int getResult()
{
for (int i = 0; i< 100 ; i++){
result = result + 1;
}
return result;
}
}
- 스레드 세이프한 경우
- 아래 코드는 synchronized를 써서 동시에 result 값을 증가시키지 못하게 하는, 스레드 세이프한 환경이다.
- (자바에서는 스레드 세이프한 환경을 구현하기 위해 임계 영역에 접근하는 메서드를 synchronized 키워드를 사용하여 동기화시킨다. Synchronized를 사용하면 해당 메서드를 수행할 때, 다른 스레드가 간섭하지 못한다.)
public class ThreadSafeClass {
private int result = 0;
public synchronized int getResult()
{
for (int i = 0; i< 100 ; i++){
result = result + 1;
}
return result;
}
}
}
- 즉 어떤 함수가 한 스레드에서 호출돼서 실행중일 때,
다른 스레드가 그 함수를 호출해서 동시에 함께 실행되더라도 각 스레드에서의 함수의 수행 결과가 서로 간섭하지 않고 정상적으로 나오는 상태를 스레드 세이프라고 한다.
Thread-safe한 환경을 만드는 방법
- Re-entrancy (재진입성)
- 어떤 함수에 여러 스레드가 동시에 접근해도 언제나 같은 실행 결과를 보장하게 한다.
- (약간 추상적인 개념, 코드 작성시 스레드끼리 독립적일 수 있게 구현하는 것을 뜻함.)
- Thread-local storage
- 공유 자원의 사용을 최대한 줄여 스레드들의 동시 접근을 막고,
- 로컬 변수를 사용하여, 각각의 쓰레드가 자신만의 변수를 가지게 한다.
- Mutual exclusion (상호 배제)
- 공유 자원에는 하나의 스레드만 접근 가능하게 한다.
- (공유자원 접근시 lock, semaphore등 동기화 기법으로 제어한다.)
- Atomic operations
- 공유 자원에 접근할 때 atomic한 연산으로 구현한다.
- (“+=” 은, ‘+’ 연산 후 ‘=’ 연산을 하기에 원자적이지 않다.)
- Immutable Object
- 공유되는 데이터라도 항상 똑같은 값이 나올 수 있도록 변경 불가능한 데이터 유형(자바에서 final 등)을 사용한다.
⇒ 확실하게 스레드 세이프한 환경을 구현할 수 있는 경우 외에는 동기화 기법을 사용한다.