Synchronization

man soup·2020년 10월 21일
0

자바 문법

목록 보기
13/15

Synchronziation(동기화)

쓰레드는 공유된 자원을 통해 의사소통
효과적이지만 2가지 에러 발생 가능 : thread interference(간섭) error와 memory consistency(일관성) error
이 두가지 에러를 예방하기 위해 synchronization을 사용
동기화를 하면 starvation 이나 livelock 같은 thread contention과 race condition을 예방할 수 있음
(race condition : 쓰레드가 어떻게 스케쥴링 되느냐에 따라 결과가 바뀌는 상황 )

Thread Interference

counter = 0;
add(){ counter++}
get(){return counter }
이경우 여러 쓰레드들이 동시에 counter변수에 접근하면 원하는 결과 x인 경우 존재
이러한 경우는 쓰레드 간섭이라고 함
ex) T1이 add T2 add 해서 결과가 counter 2가 아닌 1이 나옴

Memory Consistency Errors

여러 쓰레드가 같은 데이터에 대해 다른 뷰를 가진 상황
발생 원인은 매우 복잡
피하는 방법이 중요
happens-before 관계를 이해하는 것이 피하는 방법의 핵심
happens-before 관계란 하나의 특정한 statement에 의해 메모리 write한 것이 다른 특정 statement에게 visible함을 보장한다.
happens-before 관계를 만드는 3가지 방법
1. 동기화
2. Thread.start
새로운 쓰레드
3. Thread.join

Synchronized Methods

메소드 앞에 synchronized 키워드 붙인 것
효과 2가지
1. 같은 객체의 non-static Synchronized Methods에 하나의 쓰레드만 접근 가능
다른 쓰레드가 접근하려면 먼저 접근한 쓰레드에서 메소드 종료해야함
( a 객체의 func1이 synchronized이면 하나의 쓰레드만 접근 가능 )
2. Synchronized Methods 종료 시 같은 Synchronized Methods 호출에 대해 happens-before 관계를 형성함
즉 종료시 동기화된 메소드를 가진 객체의 상태 변화에 대해 다른 모든 쓰레드들이 visible하게 된다는 것

동기화 메소드는 쉽게 쓰레드 간섭 에러와 메모리 일관성 에러를 예방하게 해준다
효과적이지만 liveness 문제를 발생시킬 수 있다.

Intrinsic Locks and Synchronization

동기화는 intrinsic lock 또는 monitor lock이라 부르는 내부의 엔티티에 의해 만들어졌다.
(API 스펙에서 이 엔티티를 간단하게 monitor라고 부르기도 한다)
intrinsic lock은 동기화의 2가지 관점에서 역할을 한다.
1. 객체의 상태에 상호배제적으로 접근하게하기
2. happens-before 관계 형성

Locks In Synchronized Methods
쓰레드가 동기화 메소드를 호출하면 호출된 메소드의 객체에 대한 내부 락을 얻고 메소드 반환시 락도 푼다.
예외에 발생해 메소드에서 반환되도 락 품
static 동기화 메소드가 호출되면 Class 객체에 대한 내부 락을 얻는 것

public class A{
    private int c = 0;
    
    private static int d = 0;
    
    public synchronized void increment1() {
            c++;
    }

    public void increment2() {
	    synchronized(this)
        c++;
    }

    public static synchronized void increment3(){
        d++;
    }
    public static void increment3() {
        synchronized(A.class)
        d++;
    }
}

increment1 와 increment2 같은 의미
increment3 와 increment4 같은 의미

Synchronized Statements

락을 걸 객체를 지정해서 사용
객체 전체에 락을 걸지 않으므로 조심해야함

Reentrant Synchronization

다른 쓰레드가 가지고 있는 락을 획득할 수 없지만 자신이 이미 가진 락을 다시 얻을 수 있다.
같은 락을 한번이상 같는 것을 Reentrant Synchronization라고 함

Atomic Access

한번에 일어나는 것을 원자적이라고 함
중간에 멈출 수 없음, 일어나거나 일어나지 않거나 둘중 하나
레퍼런스 변수나 대부분의 원시 변수( long 과 double 제외) r/w 모두 원자적
volatile이 선언된 모든 변수는 r/w 모두 원자적
원자적 행동은 중간에 간섭받을 수 없으므로 쓰레드 간섭에서 제외됨
그러나 모든 동기화 원자 행동이 필요없는건 아님
왜냐하면 메모리 일관성 문제는 여전히 가능
volatile 변수가 메모리 일관성 에러를 줄여줄 수 있음
왜냐하면 같은 변수에 대한 후속 읽기에 대해 happen-before 관계를 형성함
즉 volatile 변수를 바꾸면 언제나 다른 쓰레드들도 바뀐걸 알 수 있음
또한 쓰레드가 volatile 변수를 읽으면 volatile 변수의 최신상태를 알 수 있을뿐만 아니라 그 변화를 일으킨 사이드 이펙트또한 알수있음
간단한 원자성 변수에 접근하는것이 동기화 코드를 통해 변수에 접근하는거보다 효과적이지만 메모리 일관성 문제 조심해야함

질문 1 ) Double Long 변수는 r/w atomic?
질문 2 ) static 변수 접근 메소드가 하나는 syn static이고 하나는 syn non-static이면 두 메소드의 락이 다름 -> thread safe 하지 않는 상태?

출처 : https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html

profile
안녕하세요

0개의 댓글