[컴][자바] 동시성, Concurrency from java tutorial

아래 내용은 ref. 1 을 번역한 내용이다.


동기화(Synchronization)


thread 들이 field 들에 대해 access 를 공유하면서 발생하는 문제가 2가지
  1. Thread interference
  2. Memory inconsistency
있다. 이것을 막는 데 필요한 도구를 synchronization 이라고 한다.



Thread Interference


c++
라는 동작도 jvm 에서는 3가지 step 으로 바뀔 수 있다.
  1. c 의 값을 가져오고
  2. 가져온 값을 1 증가 시키고
  3. c 에 새로운 값을 저장한다.
그러므로 Thread  A 에서는 c++ 을 하고 Thread B 에선 c-- 을 하게 되면,
만약 Thread A 와 Thread B 가 1step 씩 차례로 동작하게 된다면
  1. c 의 값을 가져오고(A)
  2. c 의 값을 가져오고(B)
  3. 가져온 값을 1 증가 시키고(A)
  4. 가져온 값을 1 감소 시키고(B)
  5. c 에 새로운 값을 저장한다.(A)
  6. c 에 새로운 값을 저장한다.(B)
결국 값이 엉켜 버린다.(ref. 2)


Memory consistency errors

이 녀석이 일어나는 상황은 복잡해서 문서에서 설명하지 않는다. 프로그래머는 이 memory consistency errors를 피하는 방법만 알면 된다고 이야기 한다.

이 memory consistency errors 를 피하는 핵심적인 방법은 happens-before 관계를 이해 하는 것이다.
int c = 0 ;
이 있다고 하자. 만약 thread A 가
c++
를 실행하고, thread B 가
System.println.out(c);
를 하면, Thread A 가 먼저 실행이 된다면, 화면에는 '1' 이 찍힐 것이고, Thread B 가 Thread A 보다 먼저 실행된다면 화면에는 '0' 이 찍힐 것이다.

프로그래머가 이 두 개의 statement 사이에 happens-before relationship 을 만들기 전에는 Thread A 가 Thread B 보다 먼저 실행된다는 것을 보장할 수 없다. 우리나라 말로 풀이한다면 "미리 일어나는", "선행" 정도의 의미가 되겠다.

이 happens-before relationships 를 만드는 방법이 몇 가지 있다. 그 중 하나가 synchronization 이다.

happens-before relationship 의 예는 Thead.start() 와 Thread.join() 에서도 볼 수 있다.
  • start 는 그 시점부터 thread 가 시작하는 것이기 때문에 그 이전의 statement 와 happens-before 관계 가 되는 것이고,
  • join 은 thread 가 끝날 때까지 기다려 주는 것이기 때문에, join 이후에 실행되는 statement 들은 그 이전의 statement 와 happens-before relationship 이 되는 것이다.

java 에서는
  • syncrhronized statement
  • synchronized method
를 제공한다.



Synchronized method

public synchronized void increment() {
        c++;
    }
위 처럼 synchronized 를 사용하면 된다.

synchronized method 에는 2가지 효과가 생긴다.

같은 object(instance) 에서 synchronized method 에 대해 간섭할 수 있는(interleave) 2개의 호출이 불가능하다.

만약 thread A 가 synchronized method 를 사용하고 있는데, thread B 가 호출을 한다면, thread B 는 thread A 가 끝날 때까지 기다려야 한다. (block)

synchronized method 가 존재하면, 자동적으로 synchronized 호출들 간에 happens-before 관계를 만든다. 이것은 모든 thread 가 object 의 상태의 변화를 아는 것을 보장한다.(guarantee the visiblility)



Intrinsic lock and Synchronization

Synchronization 은 intrinsic lock 또는 monitor lock 으로 알려진 내부요소를 이용해서 만들어졌다.

thread 가 synchronized method 를 호출 할 때, method 의 object 의 intrinsic lock 을 자동으로 acquire 하고 method 가 return 할 때 lock 을 release 한다.(exception 에 의한 return 도 포함된다.)

static synchronized method 가 호출될 때는 staic method 는 class 와 관계가 있기 때문에, thread 는 class와 관련된 class object 에 대한 intrinsic lock 을 acquire 한다.

그래서 class 의 static field 를 접근은 class 의 instance 의 lock 과는 다른 lock 에 의해 조정된다.



Synchronized statement

synchronized code 에서 다른 object들의 method 를 부르는 것은 아래와 같은 liveness 문제(liveness problem)를 발생시킬 여지가 있다.

그러므로 이럴 때 synchronized statement 는 원하는 부분만 synchronized 할 수 있어 도움이 된다.(역자: synchronized tatement block 이 하나의 atomic operation 이 되는 것이라고 여기면 될 듯 하다.)

그리고 잘 다듬어진 synchronization 을 이용해 concurrency 를 향상시키는 데 유용하다.

public class MsLunch {
    private long c1 = 0;
    private long c2 = 0;
    private Object lock1 = new Object();
    private Object lock2 = new Object();
    public void inc1() {
        synchronized(lock1) {
            c1++;
        }
    }
    public void inc2() {
        synchronized(lock2) {
            c2++;
        }
    }
}

에서 c1 과 c2 가 "각각"(따로) 동기화돼서 업데이트 되어야 하는 경우에 this 를 synchronized 하거나, method 에 synchronized 를 걸어서 object 를 lock 하는 방법을 선택하지 않고, lock 을 제공할 2개의 object 를 생성해서 c1과 c2 가 서로 update 할 때 영향을 주지 않도록 위와 같이 사용할 수 있다.



Liveness 정의

컨커런트 어플리케이션(concurrent application)의 적시에(in a timely manner) 실행되는 능력
을 그 어플리케이션의 liveness 라고 말한다. 이 때 발생할 수 있는 문제는 아래와 같다.
  • liveness 문제의 종류(liveness problem)
    • deadlock
    • starvation
    • livelock


Reference

  1. http://docs.oracle.com/javase/tutorial/essential/concurrency/interfere.html
  2. http://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html

댓글 없음:

댓글 쓰기