반응형
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- kotlin강좌
- 병렬프로그래밍
- android
- 자바
- viewmodel
- 책
- 코틀린
- g 단위테스트
- 알게되는
- 디자인패턴
- Coroutine
- ReactiveProgramming
- 안드로이드스튜디오
- 안드로이드강좌
- mockito
- theming
- k8s
- 알고리즘
- Kotlin
- Rxjava
- 병럴프로그래밍
- 회고
- 안드로이드
- 스레드
- 코루틴
- 글또
- Gradle
- 커스텀상태
- 테스트
- Compose
Archives
- Today
- Total
선생님, 개발을 잘하고 싶어요.
자바 병렬 프로그래밍 - 1부 2장 - 스레드 안정성 본문
- 공유되고 변경할 수 있는 상태에 대한 접근을 관리하는 것이다.
- 공유됐다 👉 여러 스레드가 특정 변수에 접근할 수 있다.
- 변경할 수 있다(mutable) 👉 해당 변수 값이 변경될 수 있다는 뜻이다.
- 스레드 안정성은 코드를 보호하는 게 아니라, 데이터에 제어 없이 동시 접근하는 걸 막으려는 의미
- 스레드가 하나 이상 변수에 접근하고 + 변수에 값을 쓰면 👉 해당 변수에 접근할 때 관련된 모든 스레드가 동기화를 통해 조율해야 한다.
- 🌟 어떤 스레드가 변경할 수 있는 상태 변수를 적절한 동기화 없이 접근한다면 그 프로그램은 잘못됐다. 이런 문제를 해결하는 3가지 방법이 있다.
- 변수를 스레드 간에 공유하지 않는다. (스택에 저장된 변수 접근 등)
- 변수를 변경할 수 없도록 만든다.
- 접근할 땐 언제나 동기화를 사용한다.
스레드 안전성이란?
- 여러 스레드가 클래스에 접근할 때 계속 정확하게 동작하면 해당 클래스는 안전하다.
- 🌟 스레드 안전한 클래스?
- 클라이언트 쪽에서 별도로 동기화할 필요가 없도록 동기화 기능도 캡슐화
- 🌟 상태 없는 객체는 항상 스레드 안전하다.
단일 연산
경쟁 조건 (race condition)
- 타이밍이 딱 맞았을 때만 정답을 얻는 경우
- 경쟁 조건 형태
- 점검 후 행동 (check-then-act)
- 관찰 결과의 무효화, 즉 잠재적으로 유효하지 않은 관찰 결과로 결정을 내리거나 계산하는 것
- e.g. 파일 X 가 없음을 확인하고 파일 X를 생성한다. 하지만 그 사이에 다른 누군가가 파일 X를 이미 생성했을 수 있다. 이런 경우 문제가 발생한다.
- 읽고 수정하고 쓰기 (read-modify-write)
- 이전 상태 기준으로 객체의 상태를 변경한다.
- e.g. 부주의한 싱글톤 구현
class LazyInitRace { private var instance: LazyInitRace? = null fun getInstance(): LazyInitRace { if (instance == null) { // LazyInitRace 객체를 생성하기 전에 다른 Thread 에서도 // 이 조건문을 타고 들어올 수 있다. instance = LazyInitRace() } return instance } }
- 없으면 추가하는 (put-if-absent)
- if (!vector.contains(elem)) vector.add(elem)
- 점검 후 행동 (check-then-act)
- 경쟁 조건을 피하려면 변수가 수정되는 동안 다른 스레드가 해당 변수를 사용하지 못하도록 막을 방법이 있어야 한다.
- 다른 스레드는 수정 도중에 해당 변수에 접근할 수 없다. 즉, 수정 이전이나 이후에만 상태를 읽거나 변경할 수 있다는 뜻이다.
복합 동작 (compound action)
- 스레드 안전성을 보장하기 위해서는 점검 후 행동 (check-then-act), 읽고 수정하고 쓰기 (read-modify-write) 동작이 항상 단인 연산이어야 한다.
- 위와 같은 동작을 복합 동작이라고 한다.
- 복합 동작 👉 스레드에 안전하기 위해서는 전체가 단일 연산으로 실행돼야 하는 일련의 동작
락 (lock)
- 여러 개의 변수가 하나의 불변조건을 구성하고 있다면, 이 변수들은 서로 독립적이지 않다.
- 각 변수들 그 자체로 스레드 안전하다고 해서 전체 클래스의 상태가 안전하지는 않다는 뜻이다.
- 🌟 상태를 일관성 있게 유지하려면 관련 있는 변수들을 하나의 단일 연산으로 갱신해야 한다.
암묵적인 락
- synchronized 구문
- 자바에 내장된 락을 암묵적인 락(intrinsic lock) 혹은 모니터 락(monitor lock)이라고 한다.
- 락은
- 스레드가 synchronized 블록에 들어가기 전에 자동 확보
- 정상적으로든 예외가 발생하든 해당 블록을 벗어날 때 자동으로 해제된다.
- 🌟 한 번에 한 스레드만 특정 락을 소유할 수 있다.
- 🌟 특정 락으로 보호된 코드 블록은 한 번에 한 스레드만 실행할 수 있다.
재진입성 (reentrant)
- 재진입성 👉 스레드 단위로 락을 얻는다
- 특정 스레드가 자기가 이미 획득한 락을 다시 확보할 수 있다.
락으로 상태 보호하기
- 락은 자신이 보호하는 코드 경로에 여러 스레드가 순차적으로 접근하도록 한다.
- 따라서 공유된 상태에 한 시점에 오직 한 스레드만 접근할 수 있도록 보장하는 규칙을 만들 때 유용하다.
- 락을 활용하는 일반적인 사용 예는
- 모든 변경 가능한 변수를 객체 안에 캡슐화
- 암묵적인 락을 사용, 해당 변수에 접근하는 모든 코드 경로를 동기화함
- 객체 상태 나타내는 모든 변수는 객체의 암묵적인 락으로 보호
- 🌟 여러 변수에 대한 불변조건이 있으면, 해당 변수들은 모두 같은 락으로 보호해야 한다.
활동성과 성능
- 🌟 synchronized 블록의 범위를 스레드 안정성을 유지할 정도로만 최소로 잡자.
- 블록 안의 코드가 무엇을 하는지, 얼마나 걸릴지 파악해야한다.
- 락을 오래 잡고 있으면 활동성이나 성능 문제를 야기한다.
- 🌟 복잡하고 오래 걸리는 계산, 네트웍 작업, 사용자 입출력 작업과 같이 오래 걸리는 부분은 가능한 락을 잡지 말자.
'일상 > 책 리뷰' 카테고리의 다른 글
자바 병렬 프로그래밍 - 1부 4장 - 객체 구성 (0) | 2022.06.11 |
---|---|
자바 병렬 프로그래밍 - 1부 3장 - 객체 공유 (0) | 2022.06.10 |
자바 병렬 프로그래밍 - 0부 1장 - 개요 (0) | 2022.06.09 |
Kotlin in Action - 2부 11장 - DSL 만들기 (0) | 2022.06.07 |
Kotlin in Action - 2부 10장 - 애노테이션과 리플렉션 (0) | 2022.06.06 |
Comments