객체 공유
여러 개의 스레드에서 객체를 동시에 사용하려 할 때 섞이지 않고 안전하게 동작하도록 객체를 공유하고 공개하는 방법을 살핀다.
변수를 사용하고 있을 때 다른 스레드가 해당 변수 값을 사용하지 못하도록 막아야한다. 동기화 블록을 빠져나가고 나면 다른 스레드가 변경된 값을 즉시 사용할 수 있어야한다.
가시성
변수에 값을 저장하고 이후에 값을 다시 읽으면, 아까 저장한 값을 가져올 수 있을거라고 예상한다. 하지만 멀티 스레드 환경에서는
메모리상 공유된 변수를 여러 스레드에서 서로 사용할 수 있게 하려면 반드시 동기화 기능을 구현해야 한다.
재배치(reordering): 특정 메소드의 소스코드가 100% 코딩된 순서로 동작함을 보장할 수 없다. 동기화를 지정하지 않으면 컴파일러나 프로세서, JVM이 프로그램 코드 실행 순서를 임의로 바꾸는 경우가 있다.
여러 스레드에서 공동으로 사용하는 변수에는 항상 적절한 동기화 기법을 적용한다
스레드는 메모리를 공유하는데, 컴파일, 런타임 최적화 시점에 메모리 이외의 저장소를 활용할 수 있는 프로세서 레지스터나 외부 캐시..
스테일 데이터 (stale data)
변수를 사용하는 모든 경우에 동기화를 시켜두지 않으면 해당 변수에 대한 최신 값이 아닌 다른 값을 사용하게 되는 경우가 발생할 수 있다. 다른 스레드에서 작성한 값을 가져갈 수 없기 때문에, 변수를 사용하려고 접근하기 전에 다른 스레드가 값을 갱신했다면, 이미 최신 값이 아닌 값으로 작업을 진행할 수 도 있다.
락과 가시성
synchronized로 둘러싸인 코드에서 스레드 A가 사용한 모든 변수 값은, 같은 락을 사용하는 스레드 B가 실행할 때 안전하게 사용할 수 있다. 락은 상호 배제뿐 아니라 정상적인 메모리 가시성을 확보하기 위해서도 사용한다.
volatile 변수
락이나 동기화 기능과는 다르다. volatile 지정은 락이나 동기화 기능이 동작하시키지 않는다. 메모리 가시성 입장에선 volatile이나 synchronized는 비슷한 효과를 가져온다. 락은 가시성 + 연산의 단일성 보장, volatile은 가시성만 보장
다음과 같은 경우만 사용하자.
공개와 유출
공개(published) -> 어떤 객체를 현재 코드의 스코프 범위 밖에서 사용할 수 있도록 만듬 이 경우에는 반드시 해당 객체를 동기화시켜야 한다.
유출 상태(escaped) -> 의도적으로 공개하지 않았지만, 외부에서 사용할 수 있게 공개된 경우 특정 객체를 공개 하면서, 그와 관련된 다른 객체까지 덩달아 공개하는 경우
내부 클래스(inner 클래스)의 인스턴스를 외부에 공개하는 경우 -> 내부 클래스 인스턴스는 항상 outer class의 참조를 가지고 있다.
스레드 한정
모든 변경가능한 객체를 다룰 때 선택할 수 있는 전략은 두 가지다.
GUI 프로그래밍에선 이벤트 스레드를 제외한 다른 스레드에서 UI 객체를 사용할 수 없다.
스레드 한정 기법은 프로그램 처음 설계부터 다뤄야한다. 프로그램 구현 내내 한정 기법을 계속 적용해야한다. 이러한 기법을 사용해도, 개발자는 스레드에 한정된 객체가 외부로 유출되지 않도록 신경 써야 한다.
스레드 한정 - 주먹구구식
GUI 모듈과 같은 특정 시스템을 단일 스레드로 동작하도록 만들 것이냐?
특정 스레드에 한정하려는 객체가 volatile로 선언되어 있다면? -> volatile 변수는 단일 스레드에서만 쓰기 작업을 한다면, 읽기 작업은 멀티 스레드에서 접근해도 안전하다. (가장 최근에 업데이트된 값을 정확하게 읽어갈 수 있다.)
스택 한정
ThreadLocal
불변성 (immutablity)
final 변수
불변 객체를 공개할 때 volatile 키워드 사용 -> 경쟁 조건을 만드는 여러 변수를 모두 모아 하나의 불변 객체로 관리하면? 경쟁 조건을 방지할 수 있다.
안전 공개 -> 특정 데이터를 여러 개의 스레드에서 사용하도록 공유할 때 적절한 동기화 방법을 적용하지 않는다면 굉장히 이상한 일이 발생할 가능성이 높다.
불변 객체와 초기화 안정성 -> 불변 객체는 별다른 동기화 적용하지 않아도, 어느 스레드건 마음껏 안전하게 사용할 수 있다.
안전한 공개 방법의 특성
불변 객체가 아닌 객체는 올바른 방법으로 안전하게 공개해야 한다.
공개하는 스레드, 사용하는 스레드 양쪽 모두에 동기화 방법을 적용
안전하게 공개? 👉 객체 참조 및 내부의 상태를 외부 스레드도 동시에 볼 수 있어야한다.
스레드 동기화 기능 갖고 있는 API
결과적으로 불변인 객체
가변 객체
mutable object를 사용할 때는 공개하는 부분, 가변 객체를 사용하는 모든 부분에 동기화 코드를 작성해야한다.
객체를 안전하게 공유하기
객체를 사용하기 전에 고려할 거\
객체를 공개할 때 -> 해당 객체를 어떤 방법으로 사용할 수 있고, 사용해야 하는지에 대해 정확한 설명 필요
병렬 프로그램에서 객체 공유 원칙
스레드 한정 -> 스레드 한정 객체는 완전하게 해당 스레드 내부에만 존재 및 그 스레드만 호출 사용 가능
읽기 전용 객체 공유
스레드 안전 객체 공유 -> 객체 내부적으로 필수 동기화 기능이 있으니 외부에서 신경쓸 필요 없다. 여러 스레드에서 마음껏 사용
동기화 방법 적용 -> 객체 접근을 위해 락 획득을 기다린다.
Last updated