2013년 12월 30일 월요일

java concurrency in practice ch3

Chapter 3.Sharing Objects

동기화는 write 만 생각하기 쉬운대 사용하는 모든 쓰레드들이 해당 데이터가 변경되었을때
해당 데이터를 볼수 있는 visibility 까지 고려해야 한다.

3.1 Visibility

there is no guarantee that the reading thread will see a value written by another thread on timely basis, or even at all. in order to ensure visibility of memory writes across threads, you must use synchronization

정확한 시간에 A 쓰레드가 쓴 값을 다른 쓰레드가 읽는것은 보장되지 않는다.
쓰레드 사이에서 메모리에 적은게 보이는걸 보장하려면 반드시 synchronization 을 사용해야한다.

public class NoVisibility {
 private static boolean ready;
 private static int number;

 private static class ReaderThread extends Thread {
  public void run() {
   while (!ready)
    Thread.yield();
   System.out.println(number);
  }
 }

 public static void main(String[] args) {
  new ReaderThread().start();
  number = 42;
  ready = true;
 }
}
1
해당  예제는 리더쓰레드에서 ready 가 되면 번호를 찍는 예제이다.
문제는 메인쓰레드에서 값을 변경한다고 해서 리더 쓰레드가 변경된 값을 본다는게
보장되지 않는다.

즉 ready 같을 보지 못해 영원이 끝나지 않거나 숫자를찍을대 0 을찍을수 있다

즉 리오더링 때문에 ready 는 보았지만 넘버는 보지 못하는경우도 있다

In the absence of synchronization, the compiler, processor, and runtime can do some downright weird things to the order in which operation appear to execute, Attempts to reason about the order in which memory actions "must" happen in insufficiently synchronized multithreaded programs will almost certainly be incorrect

동기화 의 부제는 컴파일러, 프로세서 그리고 런타입에서 operation 을 실행 하는 순서를
이상하게 할 수 있다 이러한 시도는 동기화 하지 않으면 반드시 나타나게 되고 그러므로
동기화 하지않은 멀티 쓰레드 프로그램은 대부분 정확하지 않다.

피하는 방법은? 멀티 쓰레드에서 공유 변수에 접근할때는 동기화를 잘하자......어.. 그래..- -;

3.1.1 Stale Data
동기화 하지 않는다면 stale 값을 보게 된다 문제는 어떤 변수는 최신값을 또 어떤 변수는
stale 값을 볼수도 있다는거다


@NotThreadSafe
public class MutableInteger {
 private int value;

 public int get() {
  return value;
 }

 public void set(int value) {
  this.value = value;
 }
}

@ThreadSafe
public class SynchronizedInteger {
 @GuardedBy("this")
 private int value;

 public synchronized int get() {
  return value;
 }

 public synchronized void set(int value) {
  this.value = value;
 }
}


위에 꺼는 stale 데이터이다
아래꺼는 thread safe 이다 get, set 동기화를 걸었기때문에
visibility 를 보장하기 위해서 set 에만 동기화를 걸면안된도
왜냐하면 get 할때 stale 변수를 볼 수 있기 때문이다...

3.1.2 Non-atomic 64 bit Operation
쓰레드가 읽는 값은 랜덤 밸류가 아니라 다른 쓰레드가 변경한 값이다(out-of-thin-air)

out-of-thin-air safety applies to all variables, with one exception: 64-bit numeric variables

out of thin air 세이프티는 64비트 숫자형 변수를 제외한 모든 자료형에 적용된다.
volatile 키워드를 선언하지 않는이상 jvm 은 long 이나 double 을 2번의 32-bit operation 으로 처리한다. (즉 랜덤값을 읽을 수 있다)

3.1.3. Locking and Visibility

everything A did in or prior to a synchronized block is visible to B when it execute synchronized block guard by the same lock

A가 싱크로 블락 안에 또는 전에 처리한 모든 값들이 비가 싱크로 블락에 들어간 후 (락 획득) 후에는 변경된 값을 보는게 보장된다 (물런 같은 락의로 보호되는 부분)



A 가 를 y 변경하고 락을 획득 x를 변경하고 락을 품
B 가 락을 획득 이때 y, x 는 (y 락이전에 변경, x 락안에서 변경) A의에 의해 변경된 값임이
보장된다.

Locking is not just about mutual exclusion, it is also about memory visibility. To ensure that all threads see the most-up-to-date values of shard mutable variables, the reading and writing threads must synchronized on a common lock

락은 상호배제 뿐만이 아니라 메모리 보임 과도 관련이 있다
락은 모든 쓰레드가 변경가능한 변수들의 최신값을 보는것을 보장해 준다.
(같은 락에 의해서 보호 되는 애들만)

3.1.4 Volatile variables
약한 폼의 동기화 라고 생각하면된다.
만약 volatile 이라고 선언하면 변경된 값들이 예측 가능하게 다른 쓰레들에게 전파되는것을보장한다.

만약 volatile 이라고 선언하면 컴파일러나 런타임은 해당 변수는 공유 변수이기때문에
리오더링이나 캐싱하지 않는다.  즉 volatile 변수를 읽은면 가장 최신에 변경된 값이 리턴된다

A good way of think about volatile variables is to imagine that they behave roughly like the SynchronizdInteger class

volatile 변수는 SynchronizedInteger class 와 비슷 하게 생각해도 된다.
하지만 volatile 변수는 락을 사용하지 않기때문에 쓰레드가 블락되지 안는다.

(성능은 락 보다는 싸고 일반 변수보다는 조금 비싸다)

When thread A write to a volatile variables and subsequently thread B reads that same variable, the values of all variables that were visible to A prior to writing to the volatile variables become visible to B after reading volatile variable is like entering a synchronized block

A가 쓰고 바로 B가 읽는다고 해보자
 A가 volatile 변수를 쓰기 전에 A에게 보이던 모든 변수들은
B가 volatile 변수를 읽은 다음에는 B에게 보여진다.
즉 싱크 블락에서 락을 획득하는 것과 같다.

volatile 변수는 간단하게 구현하거나 동기화 정책을 확인 할때만 사용하자
(visibility  를 보장하기 위해 쓰지말자 - 남이 알아보기 힘듬)
state을 보는걸 확신 하기 위해 사용하자(즉 확인 용이지 락이나 메모리 보임을 보장하기
위한 용도로 쓰지 말자)


volatile boolean asleep;
...
   while(!asleep)
         countSomeSheep();
1

1.만약 volatile 로 선언안한다면 다른쓰레드에서 변경할때 못알아 볼 수 있다
2.물론 락으로 구현될 수 있지만 코드가 지저분해 질수도 있다

--개발 할때 반드시 jvm 옵션으로 -server를 주자 server 옵션을 줄경우 더욱 최적화 하기 때문에 클라 jvm 에서 잘 돌던게 (최적화 되지 않아서 변수 캐쉬, 리오더등) 서버에 올라가면 동작 안하는 경우가 있다. 


the most common use for volatile variables is a completion, interruption, or status flag

*count++ 같은 경우 volatile 로 선언해 두어도 read-modify-write 를 atomic 으로 하기에는 부족하다, 만약 니가 하나의 쓰레드에서만 적는다는걸 보장할수 없다면!

locking can guarantee both visibility and atomicity; volatile variable can only guarantee visibility 

락킹은 원자성과 메모리 보임을 보장하지만 volatile 변수는 메모리 보임만 보장한다.

아래의 모든 조건이 맡을 때문 volatile 변수를 사용 할 수 있다
1.write to the variable do not depend on its current value, or you can ensure that only a single thread ever updates the value;

2 the variable dose not participate in invariants with other state variables and
3 locking is not required for any other reason while the variable is being accessed;

1.변수가 이전의 값에 의존하지 않을때 또는 하나의 쓰레드에서만 변경한다는게 보장될때
2.변수가 다른 불변성을 구성하는 상태변수에 참여하지 않고
3.변수에 접근할때 어떤이유로든  락이 보장되지 않아도 될때

사용할수 있다 .


3.2 Publication and Escape

Escape?
An object that is published where it should not have been is said to have escaped
오브젝트가 아직 준비가 되지 않아 퍼블리시 되길 원하지 않을때 퍼블리싱 되는걸 escape 라고 한다.

예제
public static Set knownSecrets;

 public void initialize() {
  knownSecrets = new HashSet();
 }

publishing one object may indirectly publish other. if you add a secret to the published knowSercets set , you've
also published that secret

오브젝트를 배포하는것 간접적으로 다른 오브젝트도 배포 할 수 있다 만약 knowSercets set 에 새 secret 를 추가하면
secret 도 배포된것이다. 비슷하게 non-private 메서드에서 오브젝트를 돌려주는것도 같다.

public class UnsafeStates {
 private String[] states = new String[] { "AK", "AL" };

 public String[] getStates() {
  return states;
 }
}


위와 같은 상황에서는 어떤 콜러도 배열의 값을 변경 할 수 있으로 해당 상태 배열은 의도된 scope 에서 escaped 되었다고 할 수 있다

public class ThisEscape {
 public ThisEscape(EventSource source) {
  source.registerListener(new EventListener() {
   public void onEvent(Event e) {
    doSomething(e);
   }
  });
 ..... do a lot of things
 initSomeValue()
 }
}

위와 같은 상황에서는 this 가 같이 퍼블리시 되었다 문제는 ThisEscape 가 아직 완전이
컨스트럭트 된게 아니다.

예를 들어보자 source에 이벤트 리스트 인스턴트 등록한다.
이제 외부에서 이벤트 발행하면 해당 이벤트
리스너가 듣게 되는대 문제는 이 이벤트 리스너가 컨스트럭터의 맨 아래 부분의 initSomeValue()에서 set 하는 변수를 사용한다고 하면
ThisEscpe 가 do a lot of thing 를 하는 동안 실행 될 수 있다

즉 컨스트럭터에서 외부에서 완전이 만들어지지 않은 this 를 사용 할 수 있게 배포한것이다.

(쓰레드를 컨스트럭터에서 실행 할때 위 와 같은 상황이 많이 발생된다.)


public class SafeListener {
 private final EventListener listener;

 private SafeListener() {
  listener = new EventListener() {
   public void onEvent(Event e) {
    doSomething(e);
   }
  };
 }

 public static SafeListener newInstance(EventSource source) {
  SafeListener safe = new SafeListener();
  source.registerListener(safe.listener);
  return safe;
 }
}
1
만약 생성자에서 쓰레드를 생성한 후 시작 시키거나 이너 클래스를 등록하고 싶으면 위와 같은 방법으로 공개적인 init or start 메서드를 만들고 private 팩토리 메서드에서 쓰레드를 생성한 후 시작할 수 있게 하자
이렇가 한다면 생성되다 만 오브젝트가 배포 되지 않는다

ThisEscape illustrates an important special case of escape when the this references escapes during construction.
When the inner EventListener instance is published, so is the enclosing ThisEscape instance. But an object is in a
predictable,consistent state only after its constructor returns,so publishing an object from with in its constructor can
publish an incompletely constructed object.This is true even if the publication is the last statement in the constructor.If
the this reference escapes during construction,the object is considered not properly constructed.[8]
[8]More specifically,the this reference should not escape from the thread until after the constructor returns.The this reference can be stored
somewhere by the constructor as long as it is not used by another thread until after construction.Safe Listener in Listing 3.8 uses this technique.
Do not allow the this reference to escape during construction.

A common mistake that can let the this reference escape during construction is to start thread from a constructor.
When an object create a thread from its constructor,it almost always shares its this reference with the new thread,
either explicitly(by passing it to the constructor)or implicitly(because the Thread or Runnable is an inner class of the
owning object).

The new thread might then be able to see the owning object before it is fully constructed. There's
nothing wrong with creating a thread in a constructor,but it is best not to start the thread immediately.Instead,expose
a start or initialize method that starts the owned thread. (See Chapter 7 for more on service lifecycle issues.)
Calling an overrideable instance method(one that is neither private nor final) from the constructor can also allow the
this reference to escape.
If you are tempted to register an event listener or start a thread from a constructor, you can avoid the improper
construction by using a private constructor and a public factory method,as shown in SafeListener in Listing 3.8.


3.3 Thread Confinement
3.4.1 Final fields
파이널 필드를 사용하면 initialization safety 를 보장 할 수 있다

Just as it is a good practice to make all fields private unless they need greater visibility
it is a good practice to make all fields final unless they need to be mutable

공게될 필요가 없으면  private 을 변경될 필요가 없으면  final 을 사용하는건 좋은 코딩 습관이다

하나라도 mutable variables 를 줄이는게 많은 것보다 관리하기 훨씬 용이하다.

3.4.2 Example: Using Volatile to Publish Immutable Object
immutable object can sometimes provide a weak form of atomicity
불변 오브젝트는 약한 원자성을 제공 할 수 도 있다

Whenever a group of related data item must be acted on atomically, consider creating an immutable holder class for them

언제든 여러개의 액션이 원자적으로 이루어 져야 한다면 불변 홀더 클래스를 만드는걸 생각해보자자

with an immutable one, once a thread acquires a reference to it, it need never worry about another thread modifying its state. if the variables are to be updated, a new holder object is created, but any threads working with the previous holder still see it in a consistent state

불변 홀더를 사용한다고 해보자 만약 쓰레드가 해당 홀더의 레퍼런스를 같는다면
다른 쓰레드가 홀더의 상태를 변경하는걸 걱정하지 않아도 된다

왜냐하면 상태를 변경하기 위해서는 홀더 자체를 새로 생성 해야 하기 때문이다
또한 생성한다고 해도 이전홀더를 보고 있던해들은 이전 홀더를 보기 때문에 일관된 상태를
볼수 있다( 원자적으로 변경되어야 되는 변수들이 동시에 움직인다.)



@Immutable
class OneValueCache {
 private final BigInteger lastNumber;
 private final BigInteger[] lastFactors;

 public OneValueCache(BigInteger i, BigInteger[] factors) {
  lastNumber = i;
  lastFactors = Arrays.copyOf(factors, factors.length);
 }

 public BigInteger[] getFactors(BigInteger i) {
        if (lastNumber == null || !lastNumber.equals(i))
}
}

@ThreadSafe
public class VolatileCachedFactorizer implements Servlet {
    private volatile OneValueCache cache =
        new OneValueCache(null, null);
    public void service(ServletRequest req, ServletResponse resp) {
        BigInteger i = extractFromRequest(req);
        BigInteger[] factors = cache.getFactors(i);
        if (factors == null) {
            factors = factor(i);
            cache = new OneValueCache(i, factors);
        }
        encodeIntoResponse(resp, factors);
    }
}

// Unsafe publication
public Holder holder;
public void initialize() {
    holder = new Holder(42);
}
1.volatile 이기 때문에 변경 될 경우 다른 쓰레드에서 볼수 있다
2.copy를 사용했기 때문에 내부 상태를 변경 할 수 없다.
3.코드를 로직에서 한번만 참조하기 때문에 문제가 생길일이 없다.

즉 락을 걸지 않아도 thread-safe 하게 할 수 있다

맨아래의 예제는 컨스트럭션은 잘되었지만 volatile 이아니기 때문에 문제가 발생된다.


public class Holder {
    private int n;
    public Holder(int n) { this.n = n; }
    public void assertSanity() {
        if (n != n)
} }

위와 같이 된다면 다른 쓰레드에서 부를때 assertSanity 가 실패 할수 있다
왜냐하면 Object class 생성자에서 일단 모든 필드에 디펄트값을 넣는다.
그후 서브클래스 생성자가 실행 되기 때문이다.

즉 쓰레드가 처음에는 0 값을 보고 그다음에 서브 생성자에서 넣은 최신값을 본다면
assertSanity 가 실패 할 수 있다.

3.5.2 Immutable Objects and Initialization Safety

Immutable objects can be used safely by any thread without additional synchronization, even when synchronization is not used to publish them

불변 오프젝트는 추가적인 동기와작업 없이 tread safe 일수 있다 심지어 publish 가 적절이
이루어 지지 않았어도

불변 오브젝트가 thread safety 를 보장 받기 위해서는
unmodifiable state, all fields are final , proper construction
변경 불가능 상태, 모든 변수가 파이널, 적절한 생성자 인대

생성자가 적절하지 않아도 보장 받을때도 있단다.

그냥 다 잘 쓰자
물런 파이널 필드가 mutable  오브젝트를 가지고 있으면 동기화에 신경써야 한다.
(예를 들어 copy 를 쓰던가 등의)

3.5.5. Safe Publication Idioms
이번에는 퍼블리싱 된 후 참조하는 쓰레드에서 변경된 값을 바로 바로 보는것에 집중해 보자

To publish an object safely, both the reference to the object and the object's state must be visible to other threads at the same time. A properly constructed object can be safely published by:
1. Initializing an object reference from a static initializer;
2. Storing a reference to it into a volatile field or AtomicReferecne
3. Storing a reference to it into a final field of properly constructed object or
4. Storing a reference to it into a field that is properly guarded by a lock

오브젝트를 안전하게 퍼블리싱하기 위해서는, 오브젝트의 참조 포인터 그리고 그 오브젝트의 상태가 다른 모든 쓰레들에게 동시에 보여지는걸 보장해야 한다.
적절하게 생성된 오브젝트는 안전하게 배포될수 있다
아래 중 하나를 만족시키면된다.
1. 오브젝트의 초기화를 static initializer 에서 한다.(필드를 스테틱으로 선언)
2. 레퍼런스를 volatile 또는 AtomicReference 에 저장 한다.
3. 적절하게 생성된 오브젝트를 final 필드에 저장하거나
4. 락으로 방어되는 필드에 저장한다.

thread safe collection 은 아래와 같은 걸 보장한다.
1.HashTable, sychronizedMap, or Concurrent-Map 에 값이나 벨류를 넣는것은
적절하게 퍼블리시 되고 모든 쓰레드가 그 맵에서 안전하게 볼수 있다
2.vactor, copyOnWrtierArrayList, Copy-OnWrite-ArraySet, SynchronizeList,SynchronizedSet 에 엘리먼트를 넣고 보는건 모두 안전하다.
3. BlockingQueue or a ConcurrentLinkedQueue 에 서 엘리먼트를 넣고 보는건 안전하다.

static 변수에서 초기화하는건 언제나 안전하다.
public static Holder holder = new Holder(42);

static initializer are executed by the JVM at class initialization time, because of internal synchronization in the JVM

3.5.4 effectively Immutable Object
 mutable object 이지만 생성된 후 변경되지 않는걸 로직상에서 보장 할 수 있는걸 effectively Immutable Object 라고 하며 당연이 추가적인 동기화 작업이 필요하지 않다.

3.5.5 Mutable objects
만약   mutable object 라면 safe publication 은 단지  배포 되었을때의 상태 값만을 보장 한다.  그 이후 object 에 접근하는 모든 작업은 락에 의해 보호 되거나 thread safe 임을 보장 할수 있어야한다.

3.5.6 Sharing Objects Safely
만약 니가 object 레퍼런스를 얻는다면 그것같다 읽기 를 할건지 쓰기를 할건지 알고 있어야한다.
그리고 오브젝트가 어떻게 접근되어야 하는지 잘 적혀야 한다.

병렬프로그래밍에서 공유 오브젝트를 사용하는 가장 좋은 정책
1.thread-confied 하나의 쓰레드에 갇혀 있게 사용
2.shared read only 말그대로 읽기만함
3.shared thread-safe 내부에서 동기화 하기 때문에 여러 쓰레드가 추가의 동기화 코드 없이 자유롭게 사용
4.Guarded 락에의해 보호 되서 해당 쓰레드에 접근하기 위해서는 락을 소유해야함