thread-safety에 대해 알아보기

참고 링크

Books

Brian Goetz, Tim Peierls, Bloch Joshua, Bowbeer Joseph, Holmes David, Lea Doug (2006). Java Concurrency in Practice. Addison-Wesley Professional.

Joshua Bloch(2018). Effective Java(3rd ed.). Addison-Wesley Professional.

Youtube Videos

CppCon 2018: Geoffrey Romer “What do you mean “thread-safe”?”

Thread Safety in Singleton

Race Conditions in Java Multithreading

[10분 테코톡] 🌷 코다의 Process vs Thread

[10분 테코톡] 알렉스, 열음의 멀티스레드와 동기화 In Java

Articles

Race condition vs. Data Race: the differences explained

Thread Safety and Shared Resources

Race Conditions and Critical Sections


백엔드 개발자 면접을 준비하면서 자주 접하게 된 키워드 thread-safety. 이번 글에서는 아래 정리한 순서에 따라 thread-safety와 관련된 여러 가지 주제들을 다뤄보려고 합니다.


- Multi-Thread Programming
- Thead-Safety란?; 무엇으로부터 'safe'하려는 걸까요?
    - Race Condition
    - Data Race
- Thread-Safety와 공유 자원; Java에서 스레드가 실행될 때 어떤 자원을 공유하게 될까요?
- Thread Control Escape Rule; thread-safety 판단 방법
- Thread-Safety 구현 방법


Multi-Thread Programming

image

모든 기술이 그렇듯 싱글 스레드 환경과 멀티 스레드 환경은 각각의 장단점을 지니고 있습니다.

싱글 스레드의 경우 하나의 프로세스에서 오직 하나의 스레드로만 실행하는 것을 의미하는데 그렇기 때문에, 하나의 레지스터와 스택으로 표현이 가능합니다.

싱글 스레드는,

반면 멀티 스레드는 CPU의 최대 활용을 위해 프로그램의 둘 이상을 동시에 실행하는 기술이고,




Thread-Safety란?

image

POSIX 공식문서, Bard, Java Concurrency in Practice 등 다양한 자료들에서 정의하는 thread-safety 개념을 정리해보면,

다수의 스레드에 의한 동시 호출에서 안정성이 보장되는 상태

를 의미합니다.

멀티 스레드 프로그래밍에서 일반적으로 어떤 함수나 변수, 혹은 객체가 여러 스레드로부터 동시에 접근이 이루어져도 프로그램의 실행에 문제가 없음을 의미하는데, 보다 엄밀하게는 하나의 함수가 한 스레드로부터 호출되어 실행 중일 때, 다른 스레드가 그 함수를 호출하여 동시에 함께 실행되더라도 각 스레드에서의 함수의 수행 결과가 올바로 나오는 것으로 정의합니다.

즉, 두 개 이상의 스레드가 Race Condition에 들어가거나 같은 객체에 동시에 접근해도 연산결과의 정합성이 보장될 수 있게끔 메모리 가시성이 확보된 상태라고 할 수 있습니다.


Race Condition; 경쟁 상태

🤔 thread-safety라는 키워드를 보면 무엇으로부터 safe, 안전하게 유지하려는 것인지 궁금증이 들 수 있습니다. 여기서 위 정의에서 언급되는 Race Condition에 대해 알아보려고 합니다.

경쟁 상태란,


경쟁 상태의 대표 유형 1) Read-Modify-Write 패턴

이전 상태를 기준으로 객체의 현재 상태를 변경하면서 발생하는 문제로,

image

예를 들어 count라는 공유자원이 있을 때

하나의 스레드가 값을 증가시키고 저장하기 직전에 다른 스레드가 증가되기 직전인 값을 읽어와서 또 증가를 시킨다면 count 변수의 결과값이 원하는 대로 증가하지 않는 상태가 발생할 수 있습니다.

경쟁 상태의 대표 유형 2) Check-Then-Act 패턴

이전에 검증(Check)한 결과가 행동(Act) 시점에는 더 이상 유효하지 않을 때 발생하는 문제인데요,

image

예시를 보시면 check 와 act 사이의 시간 차를 만들기 위해 임의로 Thread.sleep() 메서드를 활용하고 스레드 100개로 동시에 요청을 보냈을 때


Data Race; 데이터 경쟁

🤔 Race Condition과 함께 자주 언급되는 Data Race에 대해 알아보겠습니다.

데이터 경쟁이란,

참고 Race Condition vs Data Race

1) 동기화를 위한 아무 수단도 사용하지 않은 경우 => Race Condition과 Data Race 모두 발생합니다.

2) 개별 변수를 읽을 때에만 동기화 (Mutex 등의 락 or Atomic 연산을 이용)를 적용한 경우 => Race Condition은 발생하지만 Data Race는 발생하지 않습니다.

3) 전체 연산에 동기화를 적용한 경우 => Race Condition과 Data Race 모두 발생하지 않습니다.




Thread-Safety와 공유 자원

이번에는 자바에서 스레드가 실행될 때 어떤 자원을 공유하게 되며 각각의 공유되는 자원들이 thread-safety한 지 알아보도록 하겠습니다.

image

Local Variables 지역변수

Local Object References 지역 레퍼런스 변수

Object Member Variables 객체 멤버 변수




Thread Control Escape Rule

그렇다면 우리는 thread-safety를 어떻게 판단할 수 있을까요?

보통 어떤 프로그램이 스레드 안전인지 아닌지 알아내는 것은 간단하지 않지만 다음 상황들을 참고할 수 있습니다.

즉,

“만약 한 자원의 생성, 사용, 소멸이 동일한 스레드 내에서 이루어지고, 해당 스레드에서 절대 벗어나지 않는다면 이 자원의 사용은 thread-safety라 할 수 있습니다.”

image




Thread-Safety 구현 방법

그렇다면 thread-safety하게 설계하고 구현하려면 어떻게 해야할까요?

Java Concurrency in Practice

image

재진입성(Reentrancy)

상호배제(Mutual Exclusion)

스레드 지역 저장소(Thread Local Storage)

원자 연산(Atomicity)

불면 객체(Immutable Object)


정리하자면, 공유변수는 최소화하고, 사용해야 하는 공유변수가 있을 때는 최대한 캡슐화하며, 관련한 문서화를 잘 해야합니다.