우아한테크코스 테코톡
알렉스, 열음의 멀티스레드와 동기화 In Java
카테고리 : 우아한테크코스 테코톡
알렉스, 열음의 멀티스레드와 동기화 In Java
공유자원
- 공유자원과 임계영역
- 공유자원: 여러 스레드가 동시에 접근할 수 있는 자원
- 임계영역: 공유자원들 중 여러 스레드가 동시에 접근했을때 문제가 생길 수 있는 부분
- 경쟁상태
- 둘 이상의 스레드가 공유자원을 병행적으로 읽거나 쓰는 동작을 할 때 타이밍이나 접근 순서에 따라 실행 결과가 달라지는 상황
- EX) Read - Modify - Write, Check - then - act
원자성
- 월자성이란?
- 공유자원에 대한 작업의 단위가 더이상 쪼갤 수 없는 하나의 연산인 것처럼 동작하는 것
- 경쟁조건을 발생시키지 않기 위해서는 분리된 명령어들을 하나로 모아주는 과정이 필요하고 이걸 원자성이라고 한다.
- 원자성을 보장을 하게 되면 다른 쓰레드 가 한쓰레드의 연산과정에 개입을 할 수 없게 되기 때문에 경쟁상태를 방지할 수 있다.
가시성
- 쓰레드를 실행하는건 CPU가 살행을 하는데 쓰레드를 CPU가 실행을 할 때 메인 메모리에서 변수 값을 읽어와서 쓰레드를 실행을 하게 된다. 그런데 메인 메모리와 CPU 간의 거리가 좀 멀어서 CPU 캐시라는 걸 놓고 사용을 하게 되는데 CPU는 이 쓰레드를 실행할 때 필요한 값을 메인 메모리에서 읽어와서 CPU 캐시에 담아두고 CPU 캐시의 모든 연산을 반영한 다음에 그 값을 메인 메모리에 덮어쓰는 방식으로 동작을 한다.
- 메임 메모리에 있는 진짜 값을 보지 못해서 가시성이라고 한다.
- 자바에서는 가시성을 챙기기 위해서 volatile 이라는 변수를 제공한다.
- volatile 키워드를 붙여서 변수를 선언하면 이 변수는 메인 메모리에서만 값을 읽고 쓰고 하고 CPU 캐시를 사용하지 않는 변수가 된다.
동기화
- 원자성과 가시성을 챙긱는 것을 동기화라고 한다.
- 동기화에는 블록킹 방식과 논블록킹 방식이 있다.
블로킹
- 특정 스레드가 작업을 수행하는동안 다른 작업은 진행하지 않고 대기하는 방식
- EX) Monitor, Synchronized 키워드
- 모니터
- 자바에서 동기화를 하기 위한 도구
- 배타동기는 Synchronized, 조건동기는 wait(), notify(), notifyAll()
- Synchronized
- 배타 동기를 선언하는 키워드
- 연산결과가 메모리에 써질때까지 다른 스레드는 대기
- 순차적 접근을 해서 원자성과 가시성을 동시에 챙기수 있는 방식
- 단점
- 하나의 쓰레드만 임계구역에서 작업을 수행할 수 있기 때문에 나머지 대기하는 쓰레드들이 발생을 하고 성능저하로 이어질 수가 있다.
- 임계구역에 들어갈 때 락을 획득하고 들어가기 때문에 데드락이라는 문제가 발생할 수 있다.
- 데드락
- 서로 자기가 잡고 있는 자원을 놓지 않고 상대방이 가지고 있는 자원을 놓기를 계속 기다리고 있는 상태
논블로킹
- 다른 스레드의 작업여부와 상관없이 자신의 작업을 수행하는 방식
- EX) Atomic 타입
- CAS(Compare and Set)
- false를 반환을 했을 때 이후에 동작은 어떻게 할 것인지는 개발자의 요구사항에 따라 달라진다.
- EX
- while 문을 계속 돌면서 이 조건이 true가 나올 때까지 계속 재시도 하는 방법
- 몇 번 시도하가다가 exception 을 터트리고 끝내는 방법
- EX
- false를 반환을 했을 때 이후에 동작은 어떻게 할 것인지는 개발자의 요구사항에 따라 달라진다.
- Atomic 타입
- 동시성을 보장하기 위해서 자바에서 제공하는 Wrapper class
- CAS(Compare and set) + Volatile 를 활용해서 원자성과 가시성을 보장 한다.
- AtomicReference
- 일반적인 쓰레드 에서는 값을 연산을 하고자 할 때 연산하는 값을 끌어올 때는 JVM과 CPU 사이에 있는 캐시 값에서 변수를 끌어온다.
- 아토믹 래퍼런스를 설정을 하게 되면 내부에 volatile 키워드가 있어서 JVM 메모리에서 바로 쓰레드로 값을 당겨올 수가 있다.
- 연산을 할 때는 compareAndSet() 이라는 메소드가 호출이 된다.
- compareAndSet 은 CAS 알고리즘을 녹인 메소드 이다.
- 현재 메모리에 저장된 값과 쓰레드 내부의 만들어 놨던 기대값을 비교해서 일치를 하면 true 아니면 false를 반환하는 방식으로 동작한다.
- volatile 을 통해서 가시성을 CAS를 통해서 원자성을 보장한다.
스레드 안전한 객체란?
- 여러 스레드가 동시에 클래스를 사용하려 하는 상황에서 클래스 내부의 값을 안정적인 생태로 유지할 수 있다.
스레드 안전한 객체를 설계한는 법
- 공유번수 최소화 + 캡슐화 + 문서화
- 공유 변수를 최대한 안두는게 최선이다
- 내가 관리해야 될 포인트를 한 곳에 모아서 한 객체에서 캡슐화를 해가지고 그 객체만 관리할 수 있개끔 한다.
- 동기화 정책이 코드에 들어가면 코드 파악이 어려워지므로 문서화 잘 해 놓는 것도 중요하다.