netty 소스 파일 추가
- dev 에 보면 intellij를 기본 툴로 쓴단다
대충 써보니 이클립스보다 좋은거 같당 :)
1.intelJ down
2.https://code.google.com/p/msysgit/downloads/detail?name=Git-1.8.5.2-preview20131230.exe&can=2&q=
down
3.환경변수 추가
GIT_HOME
C:\Program Files (x86)\Git\bin
PATH에추가
%GIT_HOME%;
4.protocol https not supported or disabled in libcurl 해당 에러가 발생하면
C:\Windows\sysWOW64 and/or C:\Windows\System32에 libcurl.dll 의 이름을 변경
--http://stackoverflow.com/questions/17694502/libcurl-dll-error-with-git-push
5.C:\Program Files (x86)\JetBrains\Intelli\bin
idea64.exe.vmoptions 옵션 변경
idea64.exe 64비트로 실행
6.VCS 에서 네티 받기
2014년 1월 28일 화요일
2014년 1월 27일 월요일
java concurrency in practice ch 8 Applying Threads Pools
8.1.1Thread Starvation Deadlock
Whenever you submit to an Executor tasks that are not independent, be aware of the possibility of thread starvation deadlock, and document any pool sizing or configuration
constraints in the code or configuration file where the Executor is configured
Executor 에 서로 의존 하는 일을 submit 할때는 쓰레드 기아 데드락의 가능성을 인지해야한다. 그리고 Executor 의 설정 파일에 관해서 꼭 문서화 해야 한다.
아래 코드는 싱글 쓰레드 에서는 언제나 데드락이 걸린다. 또한 쓰레드 풀이 충분하지 못해 꽉차있다면 서로 기다리기때문에 데드락이 걸릴수 있다 ( 이와 같은 상황을 thread starvation deadlock) 이라고 한다.
테스크가 너무 긴게 많다면 어느순간 모든 쓰레드가 긴 테스크로 채워질꺼다
해당 사항은 문제가 발생하기 때문에 긴테스크 등에 시간제한을 두고 다시 requeue 하는 방법이 있다
Thread.join, BlockingQueue.put, countDownLatch.await, Selector.select등
8.2.Sizing Thread Pools
Whenever you submit to an Executor tasks that are not independent, be aware of the possibility of thread starvation deadlock, and document any pool sizing or configuration
constraints in the code or configuration file where the Executor is configured
Executor 에 서로 의존 하는 일을 submit 할때는 쓰레드 기아 데드락의 가능성을 인지해야한다. 그리고 Executor 의 설정 파일에 관해서 꼭 문서화 해야 한다.
아래 코드는 싱글 쓰레드 에서는 언제나 데드락이 걸린다. 또한 쓰레드 풀이 충분하지 못해 꽉차있다면 서로 기다리기때문에 데드락이 걸릴수 있다 ( 이와 같은 상황을 thread starvation deadlock) 이라고 한다.
public class ThreadDeadlock {
ExecutorService exec = Executors.newSingleThreadExecutor();
public class RenderPageTask implements Callable {
public String call() throws Exception {
Future[String] header, footer;
header = exec.submit(new LoadFileTask("header.html"));
footer = exec.submit(new LoadFileTask("footer.html"));
String page = renderBody();
// Will deadlock -- task waiting for result of subtask
return header.get() + page + footer.get();
}
}
}
8.1.2 Long-running Tasks테스크가 너무 긴게 많다면 어느순간 모든 쓰레드가 긴 테스크로 채워질꺼다
해당 사항은 문제가 발생하기 때문에 긴테스크 등에 시간제한을 두고 다시 requeue 하는 방법이 있다
Thread.join, BlockingQueue.put, countDownLatch.await, Selector.select등
8.2.Sizing Thread Pools
2014년 1월 21일 화요일
java concurrency in practice ch7 Cancellation and shutdown
쓰레드가 stop 할때 바로 정지 된다면 작업 중이던 공유 변수들에 문제가 생길 수있다
그러므로 처음부터 thread 가 gracefully 하게 stop 될 수 있도록 하는게 중요하다
7.1 Task Cancellation
자바에서는 쓰레드를 직접적으로 바로 stop 하는 방법은 존재하지 않는다
다른 쓰레드가 interrupt 를 걸어서 cancel 을 요청하는 방법이나
코드로 구현하는 방법이 존재한다.
아래는 cancel 이 호출 될 경우 flag 를 변경하고 쓰레드에서 일을 처리하기 직전에 한번씩
확인 하는 방법으로 구현한 코드이다.
쓰레드에서 사용하는 인스턴스 변수들은 동기화를 유념하자
두번째 코드는 실행 시키는 코드인대 prime 이 1초 간 돌게 this(두번째 코드를 호출하는) 를 재운다 그 후 finally 에서 무조건 cancel을 호출해서 정지하게 만든다.
7.1.1 Interruption
문제는 blockingQueue.put 같은 경우는 블락이기 떄문에 체크하는 로직을 넣을 수 가 없다.
만약 생산자가 소비자 보다 빨라서 큐를 다 채우고 put 을 호출 한다면 블락 될것이다.
(즉 체킹로직으로 가지 못한다)
There is nothing in the API or language specification that ties interruption to any specific cancellation semantics, but in practice, using interruption for anything but cancellation is fragile and difficult to sustain in larger application
언어 스펙 자체에는 캔슬 하기 위해 인터럽트를 사용 할수 있다는 말은 없다 하지만 실전에서는 사용할수 있지만 사용한다면 대규모 어플리케이션을 유지하기 어렵고 쉽게 부셔지게 한다.
각각의 쓰레드는 boolean 값의 interrupted status 값을 가지고 있다.
isInterrupted 는 현재 인터럽트 상태값을 리턴
static interrupted 는 인터럽트 상태값을 지우고 이전 상태값을 리턴한다(주의하자 만약 인터럽트 상태를 지울께 아니라면 익셉션을 던지던 무엇가를 하자 )
인터럽트 요청을 하는건 목표 쓰레드가 멈춘다는 뜻은 아니다 단지 멈출래 라는 메시지를 전달할 뿐이다.
인터럽트 당한 쓰레드는 바로 멈추는게 아니라 자기가 편할때 해당 상태를 채크하고 멈춘다.
wait, sleep join 같은 경우는 인터럽트 요청을 심각하게 고려하고 리퀘스트가 들어오면 익셉션을 던지려고 한다.
Interruption is usually the most sensible way to implement cancellation
인터럽트는 캔슬을 구현하기 위한 가장 실용적인 방법일때가 많다.
아래 코드는 인터럽트를 사용한 코드이다
1.while 문 조거네서 체크되면
2.put 콜을 부를때 체크된다.
블락킹 메써드등을 인터럽트용으로 사용할때 충분한 반응이 오지 않은다면 1번같이
선언에서 사용하는것도 좋을수 있다
7.1.2 Interruption Policies
task 가 취소 정책을 가지고 있듣이 thread 도 인터럽션 정책을 가지고 있어야 한다.
인터럽션 정책이란? 쓰레드가 인터럽트 상태를 확인했을떄 어떻게 대처 할꺼냐 라는거다
인터럽션 정책의 예러 바로 종료하거나 , 필요한걸 지우고 종료하거나, 다른 애한태 전달하거나 등의 방법이 있다
쓰레드가 인터럽트에 반응하는 방법, 테스크가 인터럽트에 반응하는 방법 즉 2개로 구분하는게 중요하다.
인터럽트는 cancel the current task, shut down the worker thread 즉 2개의 중의적 으미를 가진다.
쓰레드 풀에서 쓰레드를 가져다 쓸때는 이 인터럽트가 해당 쓰레드에게 책임이 있는지 확인해야한다. 만약 자기께 아니면 그냥 내버려둬야 한다.
(내가 남의 집을 봐주고 있을때 메일이 오면 버리지 않고 나중에 집 주인이 오면 처리하게 모아두는것과 같은 이치)
대부분의 블락킹 라이블러리 들이 그냥 인터럽트 익셉션을 던져버리는건 위와 같은 이유다
테스크의 같은경우 인터럽트되었을때 취소가 아니라 연기일 수도 있다
정책이 여러개 일수 있음으로 확신할수 없으면 익셉션을 던지고 Thread.currentThread().interrupt()로 다시 상태를 복원한다.
테스크가 쓰레드가 인터럽트 를 어떻게 처리할지 모르는것처럼 쓰레드도 테스크가 어떻게 처리 할지 모른다.
즉 인터럽션 정책은 쓰레드를 소유한 애만 처리하고 해당 부분을 encapsulation 하는게 좋다
각각의 쓰레드는 자신만의 인터럽션 정책을 가지고 있음으로 그 쓰레드를 인터럽트 했을때
정확이 어떤일이 행해질지 모른다면 인터럽트 걸지 말자
7.1.3 Responding to Interruption
인터럽트 블락킹 메서드 (thread.sleep, BlockingQueue.put) 를 호출할때 아래와 같이 두가 전략을 상용 할 수 있다
1. propagate the exception
2. restore the interruption status so that code higher up on the call stack can deal with it
1.번 전략은 아래와 같이 쉽게 구현 할수있다
BloclingQueue<Task> queue;
public Task getNextTask() throws InterruptionException{
return queue.take()
}
인터럽트 익셉션을 전파 할수없다면(하기 싫거나 Runnable 일 경우)
가장 간단한 방법은 Thread.currentThread.interrupt 를 호출 하는거다
확신 할 수 없다면 인터럽트 익셉션을 먹으면 안된다.
대부분의 쓰레드는 인터럽트 상태를 유지 하는게 맞다
Only code that implements a thread's interruption policy may swallow an interruption request. General-purpose task and library code should never swallow interruption requests
쓰레드중 인터럽트 정책을 구현한 애들만 인터럽트 요청을 무시 할수 있다 일반적인 목적의 일 또는 라이블러리들은 절대로 인터럽트 요청을 무시하면 안된다.
캔슬레이션 정책을 가지지 않지만 루프 안에서 인터럽트를 발생 시킬수 있는 콜을 호출 할 수 있는 애들은 로컬 변수를 두고 마지막에 finally 블락으로 상태값을 변경해야한다.
왜냐하면 무한 루프에 걸릴수 있기 때문이다.
언제 인터럽트 상태를 체크 하냐는 적시성이 중요하냐 성능이 중요하냐로 결정해야 된다.
만약 인터럽트 상태를 체크하는 로직이 있다면 꼭 동기화 해라
7.1.4 Examlpe:TimedRun
아래의 코드는 ScheduledExecutorService의 예제이다 1시간동안 10초에 한번씩 삑 이라고
울리는 코드를 작성하려면 아래와 같이 하면된다.
아래는 쓰레드에서 정해진 시간후에 일을 취소 하려고 시도한 코드이다
위코드에서 문제는 여러곳에서 발생한다.
1.현재 쓰레드의 인터럽트 정책을 알지 못함으로 인터럽트를 하면 안된다.
2.만약 현재 쓰레드가 정해진 시간보다 먼저 끝나면 콜러에게 결과값이 리턴된 후 그 후 인터럽트가 호출 된다.
이렇게 될 경우 어떤 일이 일어날지 알수 없다.
아래 방식은 1.익셉션을 전파하고 2.완료될때까지 기달리고 리턴하는 코드다 (join 쓰레드가 죽을때까지 블락이 걸림)
안한다.
캔슬이 불렸다고 캔슬되는게 아니다 단지 interrupt 요청을 할뿐이다.
Executer 의 interruption police 는 task 가 필요없을 캔슬을 사용해 interrupt 거는걸 허용한다.
그러므로 Executer 서비시를 사용할때 캔슬을 사용하는건 괞찬다.
7.2.Stopping a Thread based Service
쓰레드를 멈출때 절때 쓰레드를 소유하고 있지 않은애들은 멈추지 말자
executer 서비스를 사용한다면 무조건 executer service 가 처리하게 해야한다.
7.2.1.Example: A Logging Service
어플리케이션에서 로깅을 쓴다고 해보자
여러명의 생산자와 한명의 소비자 모델로 구현한 로깅이다.
아래 코드는 그냥 interrupt 요청을 받으면 바로 writer 를 받고 에러를 뱉으면서 빠져나간다.
2. 소비자에게만 interrupt 가 있다 (생산자의 경우 멀티기때문에 어렵다)
조금 느리고 바로 다운 되면 조금 빠르다 트레이드 오프다 .
*문제는 내부 쓰레드에서 락을 this 에 거는 코드인대.. name 을 찍어보면 내부 쓰레드에 걸린다. 해당 락 먼가 이상하다 나중에 찾아보자
아래는 아예 executerService 에게 밀어 버린 코드이다.
포이즌 필
말그대로 큐에 특정 메시지가 들어오면 생산자는 독약을 넣고 더이상 큐에 안보내고
소비자는 독약을 보면 더이상 안들어오니 다운되자 라고 생각하는 방법
생산자 가 몇명이고 소비자가 몇명인지 알때 쓸수 있다
예를 들어 생산자가 n 명이면 n개의 필이 소비자가 죽는다.
또는 소비자가 n명이면 생산자가 n개의 큐에 집어넣으면 모든 n 소비자가 죽는다.
독약 패턴은 큐가 unbounded 일때만 사용가능하다.
7.2.4 Example: A One-shot Execution Service
만약 메서드가 베치성 일을 하고 있고 모든 테스크가 끝난 후 결과 같이 리턴된다면 메서드에 묶인 Executer 서비스를 사용해서 쉽게 처리 가능하다.
해당 메서드들의 결과를 리턴하기 전까지 블락 되어야 하므로 exex.await를 호출하면 모든 결과같이 리턴될때까지 블락된다 :)
7.2.5 Limitations of shutdownNow the task as complete
ExecuterService 가 shutdhowNow 로 죽을때 실행 시키지 않은 테스크를 리턴한다.
하지만 실행 중이지만 완료가 되지 않은 테스크를 리턴하지 않는다.
아래는 해당 이슈를 피하기 위해 구현한 코드이다.
문제는 실제로는 완료 되었지만 완료되지 않았다 라고 나올수 있다
(마지막 명령어가 실행되고 쓰레드풀은 완료 되었다고 했는대 그순간 셋에도 더해질수 있기 때문에)
즉 2번 실행해도 문제없는 코드에만 사용해야 하는 코드이다.
요약 : 자바는 쓰레드를 죽이는데 정확한 메커니즘이 존재하지 않는다.
사용자가 여러 메커니즘을 합쳐 안전하게 죽도록 해야한다.
FuterTask, Executer 가 해당 작업을 조금 쉽게 해준다.
... 진짜 길다 ch7...;;;
그러므로 처음부터 thread 가 gracefully 하게 stop 될 수 있도록 하는게 중요하다
7.1 Task Cancellation
자바에서는 쓰레드를 직접적으로 바로 stop 하는 방법은 존재하지 않는다
다른 쓰레드가 interrupt 를 걸어서 cancel 을 요청하는 방법이나
코드로 구현하는 방법이 존재한다.
아래는 cancel 이 호출 될 경우 flag 를 변경하고 쓰레드에서 일을 처리하기 직전에 한번씩
확인 하는 방법으로 구현한 코드이다.
@ThreadSafe
public class PrimeGenerator implements Runnable {
@GuardedBy("this")
private final List[biginteger] primes = new ArrayList[biginteger]();
private volatile boolean cancelled;
public void run() {
BigInteger p = BigInteger.ONE;
while (!cancelled ) {
p = p.nextProbablePrime();
synchronized (this) {
primes.add(p);
}
}
}
public void cancel() { cancelled = true; }
public synchronized List[biginteger] get() {
return new ArrayList[biginteger](primes);
}
}

public List[biginteger] aSecondOfPrimes() throws InterruptedException {
PrimeGenerator generator = new PrimeGenerator();
new Thread(generator).start();
try {
SECONDS.sleep(1);
} finally {
generator.cancel();
}
return generator.get();
}
매번 숫자를 증가 시키기전에 flag 를 확인한다. (꼭 volatile 을 사용하자 -변경을 다른쓰레드에서 한다.)쓰레드에서 사용하는 인스턴스 변수들은 동기화를 유념하자
두번째 코드는 실행 시키는 코드인대 prime 이 1초 간 돌게 this(두번째 코드를 호출하는) 를 재운다 그 후 finally 에서 무조건 cancel을 호출해서 정지하게 만든다.
7.1.1 Interruption
문제는 blockingQueue.put 같은 경우는 블락이기 떄문에 체크하는 로직을 넣을 수 가 없다.
만약 생산자가 소비자 보다 빨라서 큐를 다 채우고 put 을 호출 한다면 블락 될것이다.
(즉 체킹로직으로 가지 못한다)
There is nothing in the API or language specification that ties interruption to any specific cancellation semantics, but in practice, using interruption for anything but cancellation is fragile and difficult to sustain in larger application
언어 스펙 자체에는 캔슬 하기 위해 인터럽트를 사용 할수 있다는 말은 없다 하지만 실전에서는 사용할수 있지만 사용한다면 대규모 어플리케이션을 유지하기 어렵고 쉽게 부셔지게 한다.
각각의 쓰레드는 boolean 값의 interrupted status 값을 가지고 있다.
isInterrupted 는 현재 인터럽트 상태값을 리턴
static interrupted 는 인터럽트 상태값을 지우고 이전 상태값을 리턴한다(주의하자 만약 인터럽트 상태를 지울께 아니라면 익셉션을 던지던 무엇가를 하자 )
인터럽트 요청을 하는건 목표 쓰레드가 멈춘다는 뜻은 아니다 단지 멈출래 라는 메시지를 전달할 뿐이다.
인터럽트 당한 쓰레드는 바로 멈추는게 아니라 자기가 편할때 해당 상태를 채크하고 멈춘다.
wait, sleep join 같은 경우는 인터럽트 요청을 심각하게 고려하고 리퀘스트가 들어오면 익셉션을 던지려고 한다.
Interruption is usually the most sensible way to implement cancellation
인터럽트는 캔슬을 구현하기 위한 가장 실용적인 방법일때가 많다.
아래 코드는 인터럽트를 사용한 코드이다
1.while 문 조거네서 체크되면
2.put 콜을 부를때 체크된다.
블락킹 메써드등을 인터럽트용으로 사용할때 충분한 반응이 오지 않은다면 1번같이
선언에서 사용하는것도 좋을수 있다
class PrimeProducer extends Thread {
private final BolcingQueue[BigInteger] queue;
primeProducer(BlockingQueue[BigInteger] queue{
this.queue = queue;
}
public void run(){
try{
BigInteger p = BigInteger.ONE;
while(Thread.currentThread().isIntruppted()){
queue.put(p = p.nextPrablePrime());
}
}catch(InterruptedException consumed){
/* Allow thread to exit */
}
}
public void cancel() { interrupt();}
}
7.1.2 Interruption Policies
task 가 취소 정책을 가지고 있듣이 thread 도 인터럽션 정책을 가지고 있어야 한다.
인터럽션 정책이란? 쓰레드가 인터럽트 상태를 확인했을떄 어떻게 대처 할꺼냐 라는거다
인터럽션 정책의 예러 바로 종료하거나 , 필요한걸 지우고 종료하거나, 다른 애한태 전달하거나 등의 방법이 있다
쓰레드가 인터럽트에 반응하는 방법, 테스크가 인터럽트에 반응하는 방법 즉 2개로 구분하는게 중요하다.
인터럽트는 cancel the current task, shut down the worker thread 즉 2개의 중의적 으미를 가진다.
쓰레드 풀에서 쓰레드를 가져다 쓸때는 이 인터럽트가 해당 쓰레드에게 책임이 있는지 확인해야한다. 만약 자기께 아니면 그냥 내버려둬야 한다.
(내가 남의 집을 봐주고 있을때 메일이 오면 버리지 않고 나중에 집 주인이 오면 처리하게 모아두는것과 같은 이치)
대부분의 블락킹 라이블러리 들이 그냥 인터럽트 익셉션을 던져버리는건 위와 같은 이유다
테스크의 같은경우 인터럽트되었을때 취소가 아니라 연기일 수도 있다
정책이 여러개 일수 있음으로 확신할수 없으면 익셉션을 던지고 Thread.currentThread().interrupt()로 다시 상태를 복원한다.
테스크가 쓰레드가 인터럽트 를 어떻게 처리할지 모르는것처럼 쓰레드도 테스크가 어떻게 처리 할지 모른다.
즉 인터럽션 정책은 쓰레드를 소유한 애만 처리하고 해당 부분을 encapsulation 하는게 좋다
각각의 쓰레드는 자신만의 인터럽션 정책을 가지고 있음으로 그 쓰레드를 인터럽트 했을때
정확이 어떤일이 행해질지 모른다면 인터럽트 걸지 말자
7.1.3 Responding to Interruption
인터럽트 블락킹 메서드 (thread.sleep, BlockingQueue.put) 를 호출할때 아래와 같이 두가 전략을 상용 할 수 있다
1. propagate the exception
2. restore the interruption status so that code higher up on the call stack can deal with it
1.번 전략은 아래와 같이 쉽게 구현 할수있다
BloclingQueue<Task> queue;
public Task getNextTask() throws InterruptionException{
return queue.take()
}
인터럽트 익셉션을 전파 할수없다면(하기 싫거나 Runnable 일 경우)
가장 간단한 방법은 Thread.currentThread.interrupt 를 호출 하는거다
확신 할 수 없다면 인터럽트 익셉션을 먹으면 안된다.
대부분의 쓰레드는 인터럽트 상태를 유지 하는게 맞다
Only code that implements a thread's interruption policy may swallow an interruption request. General-purpose task and library code should never swallow interruption requests
쓰레드중 인터럽트 정책을 구현한 애들만 인터럽트 요청을 무시 할수 있다 일반적인 목적의 일 또는 라이블러리들은 절대로 인터럽트 요청을 무시하면 안된다.
캔슬레이션 정책을 가지지 않지만 루프 안에서 인터럽트를 발생 시킬수 있는 콜을 호출 할 수 있는 애들은 로컬 변수를 두고 마지막에 finally 블락으로 상태값을 변경해야한다.
왜냐하면 무한 루프에 걸릴수 있기 때문이다.
public Task getNextTask(BlockingQueue[Task] queue) {
boolean interrupted = false;
try {
while (true) {
try {
return queue.take();
} catch (InterruptedException e) {
interrupted = true;
// fall through and retry
}
}
} finally {
if (interrupted)
Thread.currentThread().interrupt();
}
}
언제 인터럽트 상태를 체크 하냐는 적시성이 중요하냐 성능이 중요하냐로 결정해야 된다.
만약 인터럽트 상태를 체크하는 로직이 있다면 꼭 동기화 해라
7.1.4 Examlpe:TimedRun
아래의 코드는 ScheduledExecutorService의 예제이다 1시간동안 10초에 한번씩 삑 이라고
울리는 코드를 작성하려면 아래와 같이 하면된다.
public class SchedulExecuterTester {
private static ScheduledExecutorService cancelExec = Executors.newScheduledThreadPool(1);
public void beepForAnHour() {
final Runnable beeper = new Runnable() {
@Override
public void run() {
System.out.println("beep");
}
};
final ScheduledFuture[?] beeperHandle = cancelExec.scheduleAtFixedRate(beeper, 10, 10, TimeUnit.SECONDS);
cancelExec.schedule(new Runnable() {
@Override
public void run() {
beeperHandle.cancel(true);
}
}, 60 * 60, TimeUnit.SECONDS);
}
}
1아래는 쓰레드에서 정해진 시간후에 일을 취소 하려고 시도한 코드이다
private static final ScheduledExecutorService cancelExec;
public static void timedRun(Runnable r, long timeout, TimeUnit unit) {
final Thread taskThread = Thread.currentThread();
cancelExec.schedule(new Runnable() {
public void run() {
taskThread.interrupt();
}
}, timeout, unit);
r.run();
}
}
위코드에서 문제는 여러곳에서 발생한다.
1.현재 쓰레드의 인터럽트 정책을 알지 못함으로 인터럽트를 하면 안된다.
2.만약 현재 쓰레드가 정해진 시간보다 먼저 끝나면 콜러에게 결과값이 리턴된 후 그 후 인터럽트가 호출 된다.
이렇게 될 경우 어떤 일이 일어날지 알수 없다.
아래 방식은 1.익셉션을 전파하고 2.완료될때까지 기달리고 리턴하는 코드다 (join 쓰레드가 죽을때까지 블락이 걸림)
public static void timedRun(final Runnable r, long timeout, TimeUnit unit) throws InterruptedException {
class RethrowableTask implements Runnable {
private volatile Throwable t;
public void run() {
try {
r.run();
} catch (Throwable t) {
this.t = t;
}
}
void rethrow() {
if (t != null)
throw launderThrowable(t);
}
}
RethrowableTask task = new RethrowableTask();
final Thread taskThread = new Thread(task);
taskThread.start();
cancelExec.schedule(new Runnable() {
public void run() {
taskThread.interrupt();
}
}, timeout, unit);
taskThread.join(unit.toMillis(timeout));
task.rethrow();
}
code2
public static void timedRun2(Runnable r, long timeout, TimeUnit unit) throws InterruptedException {
Future task = cancelExec.submit(r);
try {
task.get(timeout, unit);
} catch (TimeoutException e) {
// task will be cancelled below
} catch (ExecutionException e) {
// exception thrown in task; rethrow
throw ThrowUtil.launderThrowable(e.getCause());
} finally {
// Harmless if task already completed
System.out.println("cancel : " + task.cancel(true));; // interrupt if running
System.out.println("isCancelled : " + task.isCancelled());
}
}
public static void main(String[] args) throws Exception{
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("thread start");
int waitMilesencondes = 3;
long start_time = System.currentTimeMillis();
while (System.currentTimeMillis() - start_time < waitMilesencondes) {
}
System.out.println("thread end");
}
};
try {
timedRun2(r, 1, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
future.cancel 은 해당 쓰레드가 실행되고 있으면 interrupt를 완료 되었다면 아무것도안한다.
캔슬이 불렸다고 캔슬되는게 아니다 단지 interrupt 요청을 할뿐이다.
Executer 의 interruption police 는 task 가 필요없을 캔슬을 사용해 interrupt 거는걸 허용한다.
그러므로 Executer 서비시를 사용할때 캔슬을 사용하는건 괞찬다.
public static void timedRun3(Runnable r, long timeout, TimeUnit unit) throws InterruptedException {
Future task = cancelExec.submit(r);
try {
task.get(timeout, unit);
} catch (TimeoutException e) {
// task will be cancelled below
} catch (ExecutionException e) {
// exception thrown in task; rethrow
throw ThrowUtil.launderThrowable(e.getCause());
} finally {
// Harmless if task already completed
task.cancel(true); // interrupt if running
}
}
위의 코드는 좋은 예제이다 시간안에 캔슬을 구현할때 위와 같이 구현하자.7.2.Stopping a Thread based Service
쓰레드를 멈출때 절때 쓰레드를 소유하고 있지 않은애들은 멈추지 말자
executer 서비스를 사용한다면 무조건 executer service 가 처리하게 해야한다.
7.2.1.Example: A Logging Service
어플리케이션에서 로깅을 쓴다고 해보자
여러명의 생산자와 한명의 소비자 모델로 구현한 로깅이다.
아래 코드는 그냥 interrupt 요청을 받으면 바로 writer 를 받고 에러를 뱉으면서 빠져나간다.
package thread.cancel;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class LogWriter {
private final BlockingQueue[String] queue;
private final LoggerThread logger;
public LogWriter(Writer writer) {
this.queue = new LinkedBlockingQueue[String](10);
this.logger = new LoggerThread(writer);
}
public void start() {
logger.start();
}
public void log(String msg) throws InterruptedException {
queue.put(msg);
}
private class LoggerThread extends Thread {
private final PrintWriter writer;
public LoggerThread(Writer writer) {
this.writer = (PrintWriter) writer;
}
public void run() {
try {
while (true)
writer.println(queue.take());
} catch (InterruptedException ignored) {} finally {
writer.close();
}
}
}
}
1. 캔슬하면 큐에 있는 모든 로그가 버려질수 있다2. 소비자에게만 interrupt 가 있다 (생산자의 경우 멀티기때문에 어렵다)
package thread.cancel;
import java.io.PrintWriter;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import Util.GuardedBy;
public class LogService2 {
private final BlockingQueue[String] queue;
private final LoggerThread loggerThread;
private final PrintWriter writer;
@GuardedBy("this")
private int reservation;
@GuardedBy("this")
private boolean isShutDown;
public LogService2(PrintWriter writer, BlockingQueue[String] queue) {
this.queue = queue;
this.writer = writer;
this.loggerThread = new LoggerThread();
}
public void start() {
System.out.println(this.getClass().getName());
loggerThread.start();
}
public void stop() {
synchronized (this) {
isShutDown = true;
}
loggerThread.interrupt();
}
public void log(String msg) throws InterruptedException {
synchronized (this) {
if (isShutDown)
throw new IllegalStateException();
++reservation;
}
queue.put(msg);
}
private class LoggerThread extends Thread {
public void run() {
try {
while (true) {
try {
synchronized (this) {
System.out.println(this.getClass().getName());
if (isShutDown && reservation == 0)
break;
}
String msg = queue.take();
synchronized (this) {
--reservation;
}
writer.println(msg);
} catch (InterruptedException e) {
/* retry */
}
}
} finally {
writer.close();
}
}
}
public static void main(String[] args) throws Exception {
LogService2 service = new LogService2(new PrintWriter("test"), new LinkedBlockingQueue[String]());
service.start();
}
}
위는 다운 시킬시 좀 이쁘게 다운할려고 노력한 클래스이다. 여기서 안전하게 다운되면조금 느리고 바로 다운 되면 조금 빠르다 트레이드 오프다 .
*문제는 내부 쓰레드에서 락을 this 에 거는 코드인대.. name 을 찍어보면 내부 쓰레드에 걸린다. 해당 락 먼가 이상하다 나중에 찾아보자
아래는 아예 executerService 에게 밀어 버린 코드이다.
public class LogService {
private final ExecutorService exec = newSingleThreadExecutor();
public void start() {}
public void stop() throws InterruptedException {
try {
exec.shutdown();
exec.awaitTermination(TIMEOUT, UNIT);
} finally {
writer.close();
}
}
public void log(String msg) {
try {
exec.execute(new WriteTask(msg));
} catch (RejectedExecutionException ignored) {}
}
}
포이즌 필
말그대로 큐에 특정 메시지가 들어오면 생산자는 독약을 넣고 더이상 큐에 안보내고
소비자는 독약을 보면 더이상 안들어오니 다운되자 라고 생각하는 방법
생산자 가 몇명이고 소비자가 몇명인지 알때 쓸수 있다
예를 들어 생산자가 n 명이면 n개의 필이 소비자가 죽는다.
또는 소비자가 n명이면 생산자가 n개의 큐에 집어넣으면 모든 n 소비자가 죽는다.
독약 패턴은 큐가 unbounded 일때만 사용가능하다.
7.2.4 Example: A One-shot Execution Service
만약 메서드가 베치성 일을 하고 있고 모든 테스크가 끝난 후 결과 같이 리턴된다면 메서드에 묶인 Executer 서비스를 사용해서 쉽게 처리 가능하다.
public class CheckMail {
public boolean checkMail(Set[String] hosts, TimeUnit unit) throws InterruptedException {
ExecutorService exec = Executors.newCachedThreadPool();
final AtomicBoolean hasNewMail = new AtomicBoolean(false);
try {
for (final String host : hosts) {
exec.submit(new Runnable() {
@Override
public void run() {
if (checkMail(host))
hasNewMail.set(true);
}
private boolean checkMail(String host) {
//*do something*/
return false;
}
});
}
} finally {
exec.shutdown();
exec.awaitTermination(10, unit);
}
return hasNewMail.get();
}
}
전부 쓰레드가 시작 된 후 shutdown()이 호출되면 해당 메서드가 완료 될때까지 기다린다.해당 메서드들의 결과를 리턴하기 전까지 블락 되어야 하므로 exex.await를 호출하면 모든 결과같이 리턴될때까지 블락된다 :)
7.2.5 Limitations of shutdownNow the task as complete
ExecuterService 가 shutdhowNow 로 죽을때 실행 시키지 않은 테스크를 리턴한다.
하지만 실행 중이지만 완료가 되지 않은 테스크를 리턴하지 않는다.
아래는 해당 이슈를 피하기 위해 구현한 코드이다.
문제는 실제로는 완료 되었지만 완료되지 않았다 라고 나올수 있다
(마지막 명령어가 실행되고 쓰레드풀은 완료 되었다고 했는대 그순간 셋에도 더해질수 있기 때문에)
즉 2번 실행해도 문제없는 코드에만 사용해야 하는 코드이다.
public class TaskTrackExecuter extends AbstractExecutorService{
private final ExecutorService exec = Executors.newCachedThreadPool();
private final Set[runnable] taskCancelledAtShutdown = (Set[runnable]) Collections.synchronizedCollection(new HashSet[runnable]());
public List[runnable] getCancelledTasks() {
if (exec.isTerminated())
throw new IllegalStateException();
return new ArrayList<>(taskCancelledAtShutdown);
}
public void execute(final Runnable runnable) {
exec.execute(new Runnable() {
@Override
public void run() {
try {
runnable.run();
} finally {
if (isShutdown() && Thread.currentThread().isInterrupted())
taskCancelledAtShutdown.add(runnable);
}
}
});
}
//delegate other ExecutorMethod to exec
아래는 위의 TaskTrackExecuter 를 이용한 webCrwaler 예제 이다.
public class WebCrawler {
private volatile TaskTrackExecuter exec;
private final Set[url] urlsToCrwal = new HashSet[url]();
public synchronized void start(){
exec = new TaskTrackExecuter();
for (URL url : urlsToCrwal)
submitCrwalTask(url);
urlsToCrwal.clear();
}
public synchronized void stop() throws InterruptedException{
try {
saveUncrwaled(exec.shutdownNow());
if(exec.awaitTermination(10, TimeUnit.MICROSECONDS));
saveUncrwaled(exec.getCancelledTasks());
}finally{
exec = null;
}
}
private void saveUncrwaled(List uncrawled) {
for (Runnable task : uncrawled)
urlsToCrwal.add(((CrwalTask)task).getPage());
}
private void submitCrwalTask(URL url) {
exec.execute(new CrwalTask(url));
}
private class CrwalTask implements Runnable{
private final URL url;
CrwalTask(URL url){
this.url = url;
}
@Override
public void run() {
for(URL link: processPage(url)){
if(Thread.currentThread().isInterrupted())
return;
submitCrwalTask(link);
}
}
private List processPage(URL url) {
/* add at queue */
return null;
}
public URL getPage(){return url;}
}
}
/* 만약 쓰레드에서 실행되는 애들이 믿을수 없는 코드라면(플러그인 같은) 아래와 같이 코딩하자 */
public void run() {
Throwable thrown = null;
try {
while (!isInterrupted())
runTask(getTaskFromWorkQueue());
} catch (Throwable e) {
thrown = e;
} finally {
threadExited(this, thrown);
}
}
/* unchecked Exception 이 나면 JVM 은 자동으로 UncaughtExceptionHandler 를 호출한다. 없으면 그냥 콘솔에 로깅) */
public class UEHLogger implements Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
Logger logger = Logger.getAnonymousLogger();
logger.log(Level.SEVERE,
"Thread terminated with exception: " + t.getName(),e);
}
}
/*아래 같은 방버으로 shutdown hook 을 걸수 있다 jvm 이 죽을때 호출되면 서로 락이 걸리는걸 막기 때문에 서비스당이 아니라 하나의 jvm 에 하나의 shutdown hook 을 거는걸 추천한다.*/
public void start() {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
try { LogService.this.stop(); }
catch (InterruptedException ignored) {}
}
});
}
요약 : 자바는 쓰레드를 죽이는데 정확한 메커니즘이 존재하지 않는다.
사용자가 여러 메커니즘을 합쳐 안전하게 죽도록 해야한다.
FuterTask, Executer 가 해당 작업을 조금 쉽게 해준다.
... 진짜 길다 ch7...;;;
2014년 1월 20일 월요일
java concurrency in practice ch6
ch6 Task Execution
6.1 Executing Tasks in Threads
일단 테스크를 논리적으로 분리해라
6.1.1 Executiong Tasks Sequentially
웹서버에서 페이지 렌더링을 구현한다고 생각해보자
또한 rederText 와 downLoadImg 같은 경우 하나는 cpu 하나는 i/o 를 사용함으로
두개로 분리해서 돌리면 더 좋은 성능을 기대 할 수 있다.
그럼 분리해보자
아래 코드는 콜어블을 사용해서 execute 에 다운 받는 부분을 따로 분리한 코드이다. 일단 다운을 시키고 텍스트를 그린다 문제는 하나의 이미지당 하나의 쓰레드에 다운받게 하면 더 빠르지 않을까?
이때는 CompletionService를 사용하자
블락킹 큐와 + executor 서비스의 조합이다.
여러일을 넘긴다. 일이 완료되면 큐에 저장된다. 없으면 블락이 걸린다.
*concurrent pakage 에서는 - 시간도 0으로 계산한다 즉 일부로 다른 코드 넣을 필요 없다.
*executor 를 공유해서 쓴다고 해도 compleationService에 몇개를 집어넣고 몇개를 꺼내올지 알수 있다 .
이번에는 시간을 생각해보자 예를 들어 화면에 광고를 표시할때 다른 서버에서 데이터를 가지고 오지 못하는걸로 느려질 필요가없다 즉 시간을 정하고 그시간안에 응답이 오지 않으면
디펄트값을 넣고 진행하던 작업을 캔슬한다.
이번에는 위와 같은게 여러개가 있다고 해보자 각각을 해야 하는가?
아니다 invokeAll 을 사용하면된다 invokeAll 의 경우 시간안에 실패하면 자동으로
future 를 캔슬 해준다.
6.1 Executing Tasks in Threads
일단 테스크를 논리적으로 분리해라
6.1.1 Executiong Tasks Sequentially
웹서버에서 페이지 렌더링을 구현한다고 생각해보자
public class SingleThreadRenderer {
void renderPage(CharSequence source) {
renderText(source);
List[ImageData] imageData = new ArrayList[ImageData]();
for (ImageInfo imageInfo : scanForImageInfo(source))
imageData.add(imageInfo.downloadImage());
for (ImageData data : imageData)
renderImage(data);
}
}
위에 소스는 지금 하나의 쓰레드에서 돌기 때문에 엄청 느리다.또한 rederText 와 downLoadImg 같은 경우 하나는 cpu 하나는 i/o 를 사용함으로
두개로 분리해서 돌리면 더 좋은 성능을 기대 할 수 있다.
그럼 분리해보자
아래 코드는 콜어블을 사용해서 execute 에 다운 받는 부분을 따로 분리한 코드이다. 일단 다운을 시키고 텍스트를 그린다 문제는 하나의 이미지당 하나의 쓰레드에 다운받게 하면 더 빠르지 않을까?
public class FutureRenderer {
private final ExecutorService executor = ...;
void renderPage(CharSequence source) {
final List[ImageInfo] imageInfos = scanForImageInfo(source);
Callable[List[ImageData]] task =
new Callable[List[ImageData]]() {
public List[ImageData] call() {
List[ImageData] result = new ArrayList[ImageData]();
for (ImageInfo imageInfo : imageInfos)
result.add(imageInfo.downloadImage());
return result;
}
};
Future[List[ImageData]] future = executor.submit(task);
renderText(source);
try {
List[ImageData] imageData = future.get();
for (ImageData data : imageData)
renderImage(data);
} catch (InterruptedException e) {
// Re-assert the thread's interrupted status
Thread.currentThread().interrupt();
// We don't need the result, so cancel the task too
future.cancel(true);
} catch (ExecutionException e) {
throw launderThrowable(e.getCause());
}
}
}
우리가 원하는건 여러개의 일을 executor 에게 넘기고 완료된 일을 하나씩 가져오는거다이때는 CompletionService를 사용하자
블락킹 큐와 + executor 서비스의 조합이다.
여러일을 넘긴다. 일이 완료되면 큐에 저장된다. 없으면 블락이 걸린다.
*concurrent pakage 에서는 - 시간도 0으로 계산한다 즉 일부로 다른 코드 넣을 필요 없다.
*executor 를 공유해서 쓴다고 해도 compleationService에 몇개를 집어넣고 몇개를 꺼내올지 알수 있다 .
public class Render{
private final ExecutorService executor;
Render(ExecutorSerivce executor){
this.executor = executor
}
void renderPage(CharSequece source){
final List[ImgInfo] info = scanForImageInfo(source);
CompletionService[ImageData] complationService = new ExecutorComplationService[ImageData](executor);
for(final ImageInfo imagInfo :info)
compleationService.submit(new Callable[ImageData](){
public ImageData call(){
return imageInfo.downLoadImg();
}
});
rederText(soruce);
try{
for(int t = 0, n = info.sizE(); t [ n ; t++){
Future[ImageData] f = complationSerivce.take();
ImageData imageData = f.get();
renderImages(imageData);
}
}catch (InterruptedException e) {
// Re-assert the thread's interrupted status
Thread.currentThread().interrupt();
// We don't need the result, so cancel the task too
future.cancel(true);
} catch (ExecutionException e) {
throw launderThrowable(e.getCause());
}
}
}
이번에는 시간을 생각해보자 예를 들어 화면에 광고를 표시할때 다른 서버에서 데이터를 가지고 오지 못하는걸로 느려질 필요가없다 즉 시간을 정하고 그시간안에 응답이 오지 않으면
디펄트값을 넣고 진행하던 작업을 캔슬한다.
page renderPageWithAd() throws IntrerruptExecption{
long endNanos = System.nanTime() + TIME_BUDGET;
Future[ad] f = exec.submit(new FetchTask());
Page page = rederPageBody();
Ad ad;
try{
long timeLeft = endNanos - System.nanoTime();
ad = f.get(timeLeft, NANOSECONDS);
}catch(ExecutionException e) {
ad = DEFAULT_AD;
}catch(TiemoutException e{
ad = DEFAULT_AD;
f.cancel(true);
}
page.setAd(ad);
return page;
}
1
이번에는 위와 같은게 여러개가 있다고 해보자 각각을 해야 하는가?
아니다 invokeAll 을 사용하면된다 invokeAll 의 경우 시간안에 실패하면 자동으로
future 를 캔슬 해준다.
private Class QuoteTask implements Callble[travelQuote]{
private final TravelCompany company;
private final TravelInfo travelInfo;
public TravelQute call() throws Exception{
return company.soliciQuote(travelinfo);
}
}
public List[TravelQuote] getRankedTravelQuotes(
TavelInfo trableInfo, set[TravelCompany] companies,Comparator[TravelQuote] ranking, long time, timeUnit, unit)
throws InterruptedExecption{
List[QutoeTask] tasks = new ArrayList[QouteTask]();
for(travelCompany company: companies)
task.add(new QuoteTask(company, travelInfo);
List[Future[TravelQuotes]] futures = exec. invokeAll(tasks, time, unit);
List[TravelQuotes] quotes = new ArraylList[TravelQuote](tasks.size());
Iterator[QuoteTask] taskIter = tasks.iterator();
for(Future[TravelQuote] f : futures){
QuoteTask task = taskIter.next();
try{
quotes.add(f.get());
}catch(ExectutionException e){
qutoes.add(task.getFailreQuote(e.getCauese);
}catch(CancellationException e){
quotes.add(task.getTimeoutQuote(e);
}
}
Collections.sort(quotes, ranking);
return quotes;
}
1
2014년 1월 16일 목요일
java concurrency in practice Summary of Part 1
1. mutable state 는 멍청하다
모든 병렬 접속 문제는 mutable state 에서 나온다 적으면 적을 수로 thread safe 를 달성하기 쉽다.
2. 왠만하면 모두 final 만들어라 꼭 mutable 일 필요 없으면
3. immutable object 는 thread-safe 이다.
immutable object 는 병렬 프로그램의 복잡성을 현저이 낮춰준다. 간단하고 빠르다 그리고 아무런 락킹 또는 카피 없이 공유 될수 있다
4. Encapsulation 은 복잡도를 현실적으로 잘 관리할수 있게 해준다.
전부 글로벌로 쓸 경우 복잡하다 왜 그렇게 하는가? Encapsulation 을 사용해서 11. 그들의 적법성을 유지 시키고 2 동기화 정책을 쉽게 유지 시키자
5. mutable variable 을 락으로 보호하자
6. 하나의 적법성 룰에 참여하는 모든 변수들은 같은 락으로 보호 되어야 한다.
7. compound action 을 할때는 lock 잡고 있자
8. mutable 변수들에게 멀티 쓰레드에서 접근할때 동기화 되어있지 않으면 잘못된 프로그램이다.
9. 동기화 하지 않아도 될꺼야라고 생각해서 동기화를 피해가지 말아라 (왠만하면 걍 동기화해라)
동기화가 필요하다고 생각하는 코드면 동기화해라( 어떤 어떤 이유 때문에 예는 안해도되 라고 하지말아라)
10. 디자인 단계에서 부터 thread - safety 를 생각하고 동기화 관련 문서를 꼭 작성해라
흠.. 섬머리 좋네.. 어째든 재미있다 ㅋㅋ 궁금해 하던 많은 부분을 책에서 얻어 가는듯
모든 병렬 접속 문제는 mutable state 에서 나온다 적으면 적을 수로 thread safe 를 달성하기 쉽다.
2. 왠만하면 모두 final 만들어라 꼭 mutable 일 필요 없으면
3. immutable object 는 thread-safe 이다.
immutable object 는 병렬 프로그램의 복잡성을 현저이 낮춰준다. 간단하고 빠르다 그리고 아무런 락킹 또는 카피 없이 공유 될수 있다
4. Encapsulation 은 복잡도를 현실적으로 잘 관리할수 있게 해준다.
전부 글로벌로 쓸 경우 복잡하다 왜 그렇게 하는가? Encapsulation 을 사용해서 11. 그들의 적법성을 유지 시키고 2 동기화 정책을 쉽게 유지 시키자
5. mutable variable 을 락으로 보호하자
6. 하나의 적법성 룰에 참여하는 모든 변수들은 같은 락으로 보호 되어야 한다.
7. compound action 을 할때는 lock 잡고 있자
8. mutable 변수들에게 멀티 쓰레드에서 접근할때 동기화 되어있지 않으면 잘못된 프로그램이다.
9. 동기화 하지 않아도 될꺼야라고 생각해서 동기화를 피해가지 말아라 (왠만하면 걍 동기화해라)
동기화가 필요하다고 생각하는 코드면 동기화해라( 어떤 어떤 이유 때문에 예는 안해도되 라고 하지말아라)
10. 디자인 단계에서 부터 thread - safety 를 생각하고 동기화 관련 문서를 꼭 작성해라
흠.. 섬머리 좋네.. 어째든 재미있다 ㅋㅋ 궁금해 하던 많은 부분을 책에서 얻어 가는듯
java concurrency in practice ch5
5.3.3.Deques and Work Stealing
디큐란?
앞뒤에서 뽑아 쓸수 있음(큐는 앞만 가능)
장점
컨슈머당 하나의 디뷰를 줌
경쟁이 일어나지 않음
컨슈머가 죽을 경우 해당 디큐에서 워크를 가져오면됨
너무 느릴경우 다른 디큐에 넣어도됨
생산자나 컨슈머가 디큐에 붙을때는 뒤에서 붙음(테일)
5.4. Blocking and Interruptible Methods
인터럽트를 먹으면 안된다
왜냐? 상위 코드에서 문제가 있는지 없는지 알수 없기 때문이다
그렇기 때문에 둘중에 하나의 로직을 사용한다.
1.외부로 집어 던지거나
2.Thread.currentThread.Intrurrept() 를 불러서 쓰레드의 상태값을 바꾼다
이렇게 한다면 상위 코드에서 필요하다면 상태값을 확인 할 수 있기 때문이다.
5.5. Synchronizers
Synchronizers는 쓰레드의 컨트롤 플루우에 참여하는 모든 것들을 지칭한다.
All synchronizers share certain structural properties:
they encapsulate state that determines whether threads arriving at
the synchronizer should be allowed to pass or forced to wait
, provide methods to manipulate that state, and provide methods to wait efficiently
for the synchronizer to enter the desired state.
5.5.1 Latches
여러개의 쓰레드가 레치의 마지막 상태값이 될때까지 기다리게 하는 Synchronizer
문처럼 움직인다 마지막 상태에 도달하기 전까지는 문이 닫혀있다
상태가 마지막 값에 도달한다면 문이 열리고 쓰레드들이 통과한다.
어떤 상태가 되기 전까지 모든 쓰레드들을 기다리게 하고 이후 한번에 실행하게 한다.
사용하는곳
리소스가 초기화대기 전까지 다른애들을 접근못하게
의존 관계에 있는 서비스들이 의존관계가 다 만들어지기 전까지 시작하지 못하게 할때
멀티플레이 게임에서 모든 사람들이 레디를 하기 전에는 시작할수 없게 하는대
CountDownLatch 말그대로 처음 시작 숫자를 주어 지고 하나씩 내려서 0이되면 실행 되게 하는 latch 의 한 종류
레치는 이벤트 기반이다 즉 카운트를 만들고 하나씩 줄여 0이되면 실행된다.
베리어는 인원기반이라고 생각해도 된다 즉 정해 놓은 모든 쓰레드가 도착하기 전까지
실행 시키지 않는다.
또한 한번 실행된 후에 다시 사용할수 있다
예를 들면 집결지에 모이고 인원이 다오면 그 후 출발한다 라고 생각하면된다.
1.future 를 이용해서 동일한 키값이 몇번 호출되지 않게 한다.
2.해쉬맵을 사용해서 쓰레드 세이프 분제를 캐쉬에 넘긴다.
3.put if absent 를 이용해서 잠깐 사이에 2번 불릴수 있는 문제를 해결한다.
4.while 문을 사용해서 캔슬 익셉션이 날경우 삭제후 다시 적재한다.(캐쉬 오염 막기)
디큐란?
앞뒤에서 뽑아 쓸수 있음(큐는 앞만 가능)
장점
컨슈머당 하나의 디뷰를 줌
경쟁이 일어나지 않음
컨슈머가 죽을 경우 해당 디큐에서 워크를 가져오면됨
너무 느릴경우 다른 디큐에 넣어도됨
생산자나 컨슈머가 디큐에 붙을때는 뒤에서 붙음(테일)
5.4. Blocking and Interruptible Methods
인터럽트를 먹으면 안된다
왜냐? 상위 코드에서 문제가 있는지 없는지 알수 없기 때문이다
그렇기 때문에 둘중에 하나의 로직을 사용한다.
1.외부로 집어 던지거나
2.Thread.currentThread.Intrurrept() 를 불러서 쓰레드의 상태값을 바꾼다
이렇게 한다면 상위 코드에서 필요하다면 상태값을 확인 할 수 있기 때문이다.
public class TaskRunnable implements Runnable {
BlockingQueue queue;
...
public void run() {
try {
processTask(queue.take());
} catch (InterruptedException e) {
// restore interrupted status
Thread.currentThread().interrupt();
}
}
}
5.5. Synchronizers
Synchronizers는 쓰레드의 컨트롤 플루우에 참여하는 모든 것들을 지칭한다.
All synchronizers share certain structural properties:
they encapsulate state that determines whether threads arriving at
the synchronizer should be allowed to pass or forced to wait
, provide methods to manipulate that state, and provide methods to wait efficiently
for the synchronizer to enter the desired state.
5.5.1 Latches
여러개의 쓰레드가 레치의 마지막 상태값이 될때까지 기다리게 하는 Synchronizer
문처럼 움직인다 마지막 상태에 도달하기 전까지는 문이 닫혀있다
상태가 마지막 값에 도달한다면 문이 열리고 쓰레드들이 통과한다.
어떤 상태가 되기 전까지 모든 쓰레드들을 기다리게 하고 이후 한번에 실행하게 한다.
사용하는곳
리소스가 초기화대기 전까지 다른애들을 접근못하게
의존 관계에 있는 서비스들이 의존관계가 다 만들어지기 전까지 시작하지 못하게 할때
멀티플레이 게임에서 모든 사람들이 레디를 하기 전에는 시작할수 없게 하는대
CountDownLatch 말그대로 처음 시작 숫자를 주어 지고 하나씩 내려서 0이되면 실행 되게 하는 latch 의 한 종류
5.5.2 Future task
래치와 비슷하게 블락한다 하지만 다른점은 callble 로 부르고 계산이 긴 애들을 단한번
계산하기 위해 사용되어진다.
상태는 waiting to run, running, completed 가 있으며 한번 copmleted 되면 상태는 계속 유지 된다.
f.get 을 할시 한번 계산된 결과는 결과를 계속 리턴한다.
*캐슁할때 참 좋은거 같다.
5.5.3 Semaphore
한번에 접근 할 수 있는 쓰레드의 수를 제한하기 위해 사용한다.
세마포어는 여러가의 접근 권한을 가지고 있다
예를 들어 3개라고 하면 쓰레드가 접근할때 하나씩 준다.
즉 3개이상은 접근하지 못한다. 한명이 release 하게 되면 다른애가 접근 할 수 있다
* set 등에 몇게까지의 저장 할 수 있는가 등에 사용된다.
public class BoundedHashSet5.5.4 Barriers{ private final Set set; private final Semaphore sem; public BoundedHashSet(int bound) { this.set = Collections.synchronizedSet(new HashSet ()); sem = new Semaphore(bound); } public boolean add(T o) throws InterruptedException { sem.acquire(); boolean wasAdded = false; try { wasAdded = set.add(o); return wasAdded; } finally { if (!wasAdded) sem.release(); } } public boolean remove(Object o) { boolean wasRemoved = set.remove(o); if (wasRemoved) sem.release(); return wasRemoved; } }
레치는 이벤트 기반이다 즉 카운트를 만들고 하나씩 줄여 0이되면 실행된다.
베리어는 인원기반이라고 생각해도 된다 즉 정해 놓은 모든 쓰레드가 도착하기 전까지
실행 시키지 않는다.
또한 한번 실행된 후에 다시 사용할수 있다
예를 들면 집결지에 모이고 인원이 다오면 그 후 출발한다 라고 생각하면된다.
public class CellularAutomata {
private final Board mainBoard;
private final CyclicBarrier barrier;
private final Worker[] workers;
public CellularAutomata(Board board) {
this.mainBoard = board;
int count = Runtime.getRuntime().availableProcessors();
this.barrier = new CyclicBarrier(count,new Runnable() {
public void run() {
mainBoard.commitNewValues();
}});
this.workers = new Worker[count];
for (int i = 0; i < count; i++)
workers[i] = new Worker(mainBoard.getSubBoard(count, i));
}
private class Worker implements Runnable {
private final Board board;
public Worker(Board board) { this.board = board; }
public void run() {
while (!board.hasConverged()) {
for (int x = 0; x < board.getMaxX(); x++)
for (int y = 0; y < board.getMaxY(); y++)
board.setNewValue(x, y, computeValue(x, y));
try {
barrier.await();
} catch (InterruptedException ex) {
return;
} catch (BrokenBarrierException ex) {
return;
}
}
}
}
public void start() {
for (int i = 0; i < workers.length; i++)
new Thread(workers[i]).start();
mainBoard.waitForConvergence();}
}
}
}
캐쉬 예제package cache;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import annotation.ThreadSafe;
import Util.ThrowUtil;
@ThreadSafe
public class Memorizer[A, V] implements Computable[A, V] {
private final ConcurrentHashMap[A, Future[V]] cache = new ConcurrentHashMap[A, Future[V]]();
private final Computable[A, V] c;
public Memorizer(Computable c) {
this.c = c;
}
@Override
public V compute(final A key) throws InterruptedException {
while (true) { //cahce pollution 이 일어 날수 있음
Future f = cache.get(key);
if (f == null) {
Callable eval = new Callable() {
@Override
public V call() throws Exception {
return c.compute(key);
}
};
FutureTask ft = new FutureTask<>(eval);
f = cache.putIfAbsent(key, ft); // atomic
if (f == null) {
f = ft;
ft.run();
};
}
try {
return f.get(); // while 문 아웃
} catch (CancellationException e) {
cache.remove(key, f); // 삭제하고 while 문으로
} catch (ExecutionException e) {
throw ThrowUtil.launderThrowable(e.getCause());
}
}
}
}
1.future 를 이용해서 동일한 키값이 몇번 호출되지 않게 한다.
2.해쉬맵을 사용해서 쓰레드 세이프 분제를 캐쉬에 넘긴다.
3.put if absent 를 이용해서 잠깐 사이에 2번 불릴수 있는 문제를 해결한다.
4.while 문을 사용해서 캔슬 익셉션이 날경우 삭제후 다시 적재한다.(캐쉬 오염 막기)
java concurrency in practice ch 4 Composing Objects
4.1 Designing a Thread-safe class
The design process for a thread-safe class should include these basic elements:
1. Identify the variables that form the object's state
2. Identify the invariants that constrain the state variables
3. Establish a policy for managing concurrent access to the object's state
thread safe 클래스를 만들기위해서는 아래 3개의 기본 규칙을 따라야 한다.
1. 오브젝트의 상태를 구성할 변수들을 식별한다.
2. 상태 변수들을 제약할 불변 조건들을 식별한다.
3. 오브젝트의 상태들에 병렬로 접근하는 것을 관리한 규칙을 세운다.
if they are all primitive type, the field comprise the entire state
if the object has fields that are reference to other objects. its state will encompass field from the referenced objects as well
오브젝트의 필드가 기본 타입이면 이 필드들이 전체 상태를 구성한다.
하지만 오브젝가 레퍼런스 필드를 가지고 있으면 상태는 레퍼런스 오브젝트가 가지고 있는 모들 필드를 포함 해야 한다.
The synchronization police defines how an object coordinates access to its state without violating its invariants or post-condition. it specifies what combination of immutability, thread confinement and locking is used to maintain thread safety
동기화 정책은 오브젝트가 불변 및 이후 조건을 침범하지 않고 오브젝트의 상태값에 조화롭게 접근 하는 방법을 정의한다.
불변성, 쓰레드 제약, 락킹이 합쳐져서 쓰레드 세이프티를 이룬다.
4.1.1 Gathering Synchronization Requirements
Making a class thread-safe mean ensuring its invariants hold under concurrent access;
this require reasoning about its state objects and variables have a state space
클래스를 쓰레드 세이프로 만드는것은 병렬접근에서 이 클래스의 값들이 적법한 값을 가져야 함을 의마한다. 해당 사항을 만족하기 위해서 상태 변수들의 상태 범위에 대한 값을 알아햐 한다.
Invariants?
Many class have invariants that identify certain states as valid or invalid
많은 클래스들은 적법함을 가지고 있다 적법함 이란 상태 변수의 값이 적절한가 아니면 적절하지 않는가 이다. 예를 들어 long 으 적절한 값은 Long.MIN~ Long.MAX 까지이다
post-condition?
operations may have post-conditions that identify certain state transitions as invalid
동작은 상태값의 변화가 적절하지 않음을 식별 할 수 있는다 post-condtion을 가질 수 있다
예를들어 count 가 17 이면 다음은 18 이 되어야 하는 경우가 있다 즉 전의 값에 의존하는
값을 가질 수 있다
you cannot ensure thread safety without understanding an object's invariants and post-conditions. Constraints on the valid values or state transitions for state variables can create atomicity and encapsulation requirements
오브젝트의 적법함과 이후 조건 에 대해서 이해하지 못한다면 쓰레드 세이프하게 만들 수없다
상태 변수들을 적법하게 만드는 제약조건들을 위해서 원자성이나 encapsulation 이 필요 할 수 도 있다
4.1.2 State-dependent Operations
state based precondition are called state dependent
empty queue 에서 remove 할 수 없다 이와 같은걸 precondition 이라고 한다.
싱글 쓰레드 프로그램에서는 프리컨디션이 되지 않으면 실패한다 하지만 멀티쓰레드 프로그램에서는 다른 쓰레드가 큐에다가 엘리먼트를 집어 넣을 수 있음으로 나중에 true 가 될 수 있다
wait-notify 는 사용하기 힘드므로 왠만하면 빌드인 라이블러리를 사용하자자
blockingQueue or semaphore
4.1.3 State OwnerShip
4.2 Instance Confinement
Encapsulation simplifies making classes thread-safe by promoting instance confinement,
often just called confinement. when object is encapsulated within another object, all code paths that have access to the encapsulated object are known and can be therefore be analyzed more easily than if that object where accessible to the entire program
오브젝트 안에 오브젝트를 위치하고 내부 오브젝트에 접근하는 모든 코드를 통제해서 쓰레드 세이프를 만드는걸 instance confinement 라고 한다.
Encapsulating data within an object confines access to the data to the object's methods
making it easier to ensure that the data is always accessed with the appropriate lock held
오브젝트 안에 encapsulating 된 데이터에 접근하는 메서드를 제약한다면 해당 데이터에 접근할때 언제나 적절한 락을 사용하는걸 쉽게 할 수 있다
위 코드를 보면 mySet 은 지금 PersonSet 에 confinement 되어 있다
HashSet 자체는 thread safe 가 아니지만 접근하는 모든 경로가 메서드에 의해 락으로 보호됨으로 쓰레드 세이프이다. 위와 같은걸 instance confinement 라고 한다
만약 Person이 mutable object라면 해당 오브젝트에서 동기화가 필요하다
예를 들어 arrayList 나 hashset 은 쓰레드 세이프가 아니다 하지만 wrapper factory 인
Collections.synchronziedList and freiend 를 이용하면 instance confinement 를 사용해 쓰레드 세이프다.
Confinement make it easier to build thread-safe classes because a class that confine its state can be analyzed for thread safety without having to examine the whole program
confinement 는 하나의 클래스만 조사해서 쓰레드 세이프임을 확인 할 수 있음으로 쓰레드 세이프를 구현하는대 편하다(사용하지 않으면 모든 프로그램을 조사해야한다)
4.2.1 the java monitor pattern
An object following the java monitor pattern encapsulates all its mutable state and guards it with the object's own intrinsic lock
자바 모니터 패턴을 따르는 오브젝트들은 그들의 mutable state 를 오브젝의 내장 락으로 보호하고 이것을 encapuslate 한다.
오브젝트 내장락을 사용하지 않고 privae lock 을 사용하면 클라이언트 코드에서 락에 접근 할 수 없으므로 조금더 튼튼하게 thread safe 를 구현 할 수 있다
intrinsic lock 이란 this 에 락을 거는걸 말하는거 같다
4.2.2 Example: tracking Fleet Vehicles
4.3 Delegating Thread Safety
The design process for a thread-safe class should include these basic elements:
1. Identify the variables that form the object's state
2. Identify the invariants that constrain the state variables
3. Establish a policy for managing concurrent access to the object's state
thread safe 클래스를 만들기위해서는 아래 3개의 기본 규칙을 따라야 한다.
1. 오브젝트의 상태를 구성할 변수들을 식별한다.
2. 상태 변수들을 제약할 불변 조건들을 식별한다.
3. 오브젝트의 상태들에 병렬로 접근하는 것을 관리한 규칙을 세운다.
if they are all primitive type, the field comprise the entire state
if the object has fields that are reference to other objects. its state will encompass field from the referenced objects as well
오브젝트의 필드가 기본 타입이면 이 필드들이 전체 상태를 구성한다.
하지만 오브젝가 레퍼런스 필드를 가지고 있으면 상태는 레퍼런스 오브젝트가 가지고 있는 모들 필드를 포함 해야 한다.
The synchronization police defines how an object coordinates access to its state without violating its invariants or post-condition. it specifies what combination of immutability, thread confinement and locking is used to maintain thread safety
동기화 정책은 오브젝트가 불변 및 이후 조건을 침범하지 않고 오브젝트의 상태값에 조화롭게 접근 하는 방법을 정의한다.
불변성, 쓰레드 제약, 락킹이 합쳐져서 쓰레드 세이프티를 이룬다.
4.1.1 Gathering Synchronization Requirements
Making a class thread-safe mean ensuring its invariants hold under concurrent access;
this require reasoning about its state objects and variables have a state space
클래스를 쓰레드 세이프로 만드는것은 병렬접근에서 이 클래스의 값들이 적법한 값을 가져야 함을 의마한다. 해당 사항을 만족하기 위해서 상태 변수들의 상태 범위에 대한 값을 알아햐 한다.
Invariants?
Many class have invariants that identify certain states as valid or invalid
많은 클래스들은 적법함을 가지고 있다 적법함 이란 상태 변수의 값이 적절한가 아니면 적절하지 않는가 이다. 예를 들어 long 으 적절한 값은 Long.MIN~ Long.MAX 까지이다
post-condition?
operations may have post-conditions that identify certain state transitions as invalid
동작은 상태값의 변화가 적절하지 않음을 식별 할 수 있는다 post-condtion을 가질 수 있다
예를들어 count 가 17 이면 다음은 18 이 되어야 하는 경우가 있다 즉 전의 값에 의존하는
값을 가질 수 있다
you cannot ensure thread safety without understanding an object's invariants and post-conditions. Constraints on the valid values or state transitions for state variables can create atomicity and encapsulation requirements
오브젝트의 적법함과 이후 조건 에 대해서 이해하지 못한다면 쓰레드 세이프하게 만들 수없다
상태 변수들을 적법하게 만드는 제약조건들을 위해서 원자성이나 encapsulation 이 필요 할 수 도 있다
4.1.2 State-dependent Operations
state based precondition are called state dependent
empty queue 에서 remove 할 수 없다 이와 같은걸 precondition 이라고 한다.
싱글 쓰레드 프로그램에서는 프리컨디션이 되지 않으면 실패한다 하지만 멀티쓰레드 프로그램에서는 다른 쓰레드가 큐에다가 엘리먼트를 집어 넣을 수 있음으로 나중에 true 가 될 수 있다
wait-notify 는 사용하기 힘드므로 왠만하면 빌드인 라이블러리를 사용하자자
blockingQueue or semaphore
4.1.3 State OwnerShip
4.2 Instance Confinement
Encapsulation simplifies making classes thread-safe by promoting instance confinement,
often just called confinement. when object is encapsulated within another object, all code paths that have access to the encapsulated object are known and can be therefore be analyzed more easily than if that object where accessible to the entire program
오브젝트 안에 오브젝트를 위치하고 내부 오브젝트에 접근하는 모든 코드를 통제해서 쓰레드 세이프를 만드는걸 instance confinement 라고 한다.
Encapsulating data within an object confines access to the data to the object's methods
making it easier to ensure that the data is always accessed with the appropriate lock held
오브젝트 안에 encapsulating 된 데이터에 접근하는 메서드를 제약한다면 해당 데이터에 접근할때 언제나 적절한 락을 사용하는걸 쉽게 할 수 있다
@ThreadSafe
public class PersonSet {
@GuardedBy("this")
private final Set mySet = new HashSet();
public synchronized void addPerson(Person p) {
mySet.add(p);
}
public synchronized boolean containsPerson(Person p) {
return mySet.contains(p);
} }
1
위 코드를 보면 mySet 은 지금 PersonSet 에 confinement 되어 있다
HashSet 자체는 thread safe 가 아니지만 접근하는 모든 경로가 메서드에 의해 락으로 보호됨으로 쓰레드 세이프이다. 위와 같은걸 instance confinement 라고 한다
만약 Person이 mutable object라면 해당 오브젝트에서 동기화가 필요하다
예를 들어 arrayList 나 hashset 은 쓰레드 세이프가 아니다 하지만 wrapper factory 인
Collections.synchronziedList and freiend 를 이용하면 instance confinement 를 사용해 쓰레드 세이프다.
Confinement make it easier to build thread-safe classes because a class that confine its state can be analyzed for thread safety without having to examine the whole program
confinement 는 하나의 클래스만 조사해서 쓰레드 세이프임을 확인 할 수 있음으로 쓰레드 세이프를 구현하는대 편하다(사용하지 않으면 모든 프로그램을 조사해야한다)
4.2.1 the java monitor pattern
An object following the java monitor pattern encapsulates all its mutable state and guards it with the object's own intrinsic lock
자바 모니터 패턴을 따르는 오브젝트들은 그들의 mutable state 를 오브젝의 내장 락으로 보호하고 이것을 encapuslate 한다.
public class PrivateLock {
private final Object myLock = new Object();
@GuardedBy("myLock") Widget widget;
void someMethod() {
synchronized(myLock) {
} }
// Access or modify the state of widget
}
there are advantage to using a private lock object instead of object's intrinsic lock(or any other public accessible lock)오브젝트 내장락을 사용하지 않고 privae lock 을 사용하면 클라이언트 코드에서 락에 접근 할 수 없으므로 조금더 튼튼하게 thread safe 를 구현 할 수 있다
intrinsic lock 이란 this 에 락을 거는걸 말하는거 같다
4.2.2 Example: tracking Fleet Vehicles
4.3 Delegating Thread Safety
2014년 1월 3일 금요일
java concurrency ch2 Thread Safety
what is state ?
an object state is it's data
an object's state encompasses any data that can affect its externally visible behavior
객체의 상태는 객체의 데이터이다
객체의 상태는 외부의 객체들이 볼수 있고 또한 그 객체에 영향을 줄 수있는 모든 데이터
what is shared?
a variable could be accessed by multiple threads;
여러 쓰레드에서 접근 가능한 변수들
what is mutable
it's value could change during its life time
life time 동안 변경 될수 있는 변수의 값
what is thread safe?
protect data from uncontrolled concurrent access
데이터를 조절 되지 않은 동시 접속에서 안전하게 보호하는것
rule for thread safe
Don't share the state variable across thread
Make the state variable immutable or
use synchronization whenever accessing the state variable
쓰레드들 끼리 상태 변수를 공유하지 말라
상태 변수를 불변하게 만들거나
상태 변수에 접근 할 때는 언제나 synchronization을 사용해라
designing thread-safe classes, good object-oriented techniques - encapsulation, and clear specification of invariants - are your best friend
where the rule is odd remember "first to make your code right,
and then make if fast"
규칙이 잘 맞지 않을때는 일단 잘돌아가게 만들고 빠르게 만들어라
(동시성 버그는 찾기 힘들고 재현하기 힘들기 때문에 작은량의 퍼포먼스 차이라면 포기하는것도 좋다)
2.1 What is Thread safety?
a class is thread-safe when it continues to behave correctly when accessed from multiple threads
쓰레드 세이프티란 여러개의 쓰레드에서 접근해도 정확하게(의도한대로) 지속적으로 동작하는걸 말한다.
a class is thread-safe if it behave correctly when accessed from multiple threads, regardless of the scheduling or interleaving of the execution of those threads by the runtime environment, and with no additional synchronization or other coordination on the part of the calling code
쓰레드 세이프리란 여러개의 멀티 쓰레드에서 접근할때 정확하게 동작하는걸 말한다.
런타임 상테에서 쓰레드의 스케쥴 또는 interleaving 되면서 실행되는거에 상관없어햔다
또한 호출하는 코드에서 동기화나 다른 조정을 할필요가 없어야 한다.
thread safe class encapsulate any needed synchronization so that clients need not provide their own.
쓰레드 세이프 클래스는 모든 동기화를 encapsulate 해야한다 그러므로 클라이언트가 동기화를 위해 다른 코드를 필요로 하면 안된다.
2.1.1 example:A Stateless Servlet
Stateless objects are always thread-safe
저장 하는 상태가 없으면 쓰레드에 대한 걱정을 하지 않아도 된다.
2.2 Atomicity
read-modify-operation
1.fetch the current value
2.add one to it
3.write the new value back
for example ++count
bad code
if the counter is initially 9, with some unlucky timing each thread could read the value
, see that is it 9, add one to it, and each set the counter to 10
9였다가 어떤 나쁜 순간에 쓰레드 두개가 동시에 9인걸 읽는다
그리고 1을 더해서 돌려 놓는다.
위와 같은 예제를 race condition 이라고 한다.
2.2.1 Race Conditions
A race condition occurs when the correctness of a computation depends on the relative timing or interleaving of multiple threads by the run times;
in other words, when getting the right answer relies on lucky timing
the most common type of race condition is check-then-act, where a potentially stale
observation is used to make a decision on what to do next
레이스 컨티션은 정확한 계산이 런타임에서 멀티플 쓰레드의 타이밍과 인터리빙에
의존해서 이루어 지는걸 말한다
다른말로는 정확한 답을 얻기 위해서는 운좋은 타이밍에 의존한다는것이다.
가장 일반적인 레이스 컨디션은 check-then-act 이다
신선하지 않은 정보(최신 데이터가 아닌)를 보고 다음에 무엇을할지 결정하는것이다.
for example race condition
you observe something to be true(file x doesn't exist) and then take action based on
that observation(create x); but in fact the observation could have become invalid
between the time you observed it and the time you act is(someone else created X in
the meantime), causing a problem(unexpected exception..)
2.2.2Example: Race Condition in lazy initialization
badCode
Say that thread A and B execute getInstance at the same time.
A see that instance is null, and instantiates a new object. B also checks if instance is
null. whether instance is null at this point depends unpredictably on timing, including the
vagaries of scheduling and how long A takes to instantiate the ExpensiveObject and set
the instance field. if instance is null when B examines it, the two callers to getInstance
may receive two different results, even though getInstance is always supposed to return
the same instance.
A 그리고 B가 동시에 getInstance 를 호출 하면 null 이 나올수 있다
이때 A가만들고 B가 만들면 언제나 하나의 인스턴스만 호출되기를 바랬지만
서로 다른 인스턴스를 돌려 줄수있다
만약 등록하는 객체 같은걸 리턴하는 오브젝트면 다른 오브젝트 등록을 할 수 있다
2.2.3 Compound Actions
Operation A and B are atomic with respect each other if, from the perspective of a thread executing A, when another tread execute B, either all of B has executed or none of it has.
an atomic operation is one that is atomic with respect to all operation, including itself, that operation on the same state
A, B operation 이 원자적이라는 것은 서로 존중 하는걸 의미한다.
B 가 실행되고 있을때 A 를 실행 시킨다면 B 는 전부 실행 되거나
아예 실행되지 않아야 한다. 원자적 operation 이란 하나의 상태에 대해서 자신을 포함한 모든operations 가 존중 받는걸 의미한다.(서로 침범하지 아니함)
what is compound actions?
we refer collectively to check-then-act and read-modify-write sequences as compound actions;
where practical, use existing thread-safe objects, like AtomicLong, to mange your class's state. it it simpler to reason about the possible states and state transitions for existing thread-safe objects than it it for arbitrary state variables, and this make it easier to maintain and verify thread safety
실전에서는 제공되는 쓰레드 세이프 오브젝트를 쓰는게 유리하다 왜냐하면 가능한 상태값과
상태의 이행 과정을 알수 있기 때문이다 직적 만들면 제 멋대로인 상태 변수들을 관리해야 한다. 즉 이미 존재한걸 쓰는게 쓰레드 세이프를 검증하기도 쉽고 유지하기도 쉽다.
2.3 Locking
아까 서블렛에 캐쉬를 달자
연속되게 같은 값이 들어온다면 캐쉬된 인수 분해 값을 리턴해주는것
(물론 이방법이 비록 효과적이지 않지만)
badCode
the definition of thread safety requires that invariants be preserved of timing or interleaving of operation in multiple thread
쓰레드 정의를 보면 멀티쓰레드 환경에서 타이밍과 잠시멈춤에도 불변해야 한다
잠시 멈춤을 기억하자!
위코드는 레스트 팩터를 바꿔는대 그때 다른쓰레드가 레스트 팩터를 실어 보내면 오류가 발
생한다.
To preserve state consistency, update related stated variables in a single atomic operation.
상태의 정합성을 유지하기 위해서는 서로 관련이 있는 상태 변수들을 한번의 원자적 operation 으로 처리해야 한다.
2.3.1 Intrinsic Locks
synchronized block has two pars: a reference to an object that will serve as the lock
and a block of code to be guard by that lock;
synchronized block 은 두개의 파트로 구성되어 있다 1. 락자체의 역활을할 오브젝트
그리고 그락이 보호하는코드 블락
이락을 intrinsic lock 또는 monitor lock 이라고 부른다.
the only way to acquire an intrinsic lock is to enter a synchronized block or method guard by that lock
내장락을 얻기 위해서는 해당 블락 또는 그락이 막고 있는 메서드에 들어가는 방법밖에 없다.
intrinsic lock act as mutax(or mutual exclusion locks)
2.3.2 Reentrancy
intrinsic locks are reentrant , if a thread tries to acquire lock that it already hols, the request succeeds
내장 락은 재입장이 가능하다 만약 락을 가진 스레드가 다리 그락을 획득 할려고 하면 그
요청은 성공한다.
reentrancy mean that locks are acquired on a per-thread rather than per-invocation
재입장은 락의 획득이 invocation 이 아니라 쓰레드당 관리 될다는걸 알수 있다
구현은 JVM 이 쓰레드가 락을 가지면 owner 와 카운터를 올린다. 재요청 하면 하나를 또 올린다. 락을 나가면 카운터를 내린다. 카운터가 0이 되면 락이 풀린다.
위의 코드를 보면 위젯을 상속해 로깅 위젯을 만들었다
하지만 내장 락이 재입장이 불가능하다면 위와 같은 메서드를 호출할때 데드락이 걸릴것이다.
2.4 Guarding State with locks
For each mutable state variable that may be accessed by more than one thread, all access to that variable must be performed with the same lock held, in this case, we say that the variable is guarded by that lock.
각각의 변경 가능한 상태의 변수들이 만약 한개이상의 쓰레드에서 접근된다면
그 변수에 접근하는 모든 락은 동일한 락으로 방어되어야한다.
이와같은 상황에 우리는 그 변수는 락에의해 방어 되고 있다고 할 수 있다.
every shared, variable should be guarded by exactly one lock. make it clear to maintainers which lock that is
모든 공유 변수들은 단 한하나의 락으로 방어 되어야한다. 그래서 유지 보수자가 어떤락이
그 역활을 하는지 쉽게 알 수 있어야 한다.
For every invariant that involve more than one variable, all the variables involved in that invariant must be guarded by the same lock
불변성에 하나 이상의 변수가 연관된다면 연관된 모든 변수들은 반드시 하나의 락으로
관리 되어야 한다.
liveness concerns the complementary goal that "something good
eventually happens".
2.5 Liveness and Performance
there is frequently a tension between simplicity and performance. when implementing synchronization policy, resist the temptation to prematurely sacrifice simplicity for the shake of performance
명료함과 성능 사이에서 고민할때가 많다 동기화 정책을 구현할때는 성능을 위해 너무 빨리
명료함을 버리려는 유혹에 빠지지 말자
avoid holing locks during lengthy computation or operations at risk of not completing quickly such as network or console I/O
일찍 끝나지 않는 network 또는 IO 를 하는 오퍼레이션 또는 긴계산 시간이 필요한 코드들은
락을 걸때 피하자
an object state is it's data
an object's state encompasses any data that can affect its externally visible behavior
객체의 상태는 객체의 데이터이다
객체의 상태는 외부의 객체들이 볼수 있고 또한 그 객체에 영향을 줄 수있는 모든 데이터
what is shared?
a variable could be accessed by multiple threads;
여러 쓰레드에서 접근 가능한 변수들
what is mutable
it's value could change during its life time
life time 동안 변경 될수 있는 변수의 값
what is thread safe?
protect data from uncontrolled concurrent access
데이터를 조절 되지 않은 동시 접속에서 안전하게 보호하는것
rule for thread safe
Don't share the state variable across thread
Make the state variable immutable or
use synchronization whenever accessing the state variable
쓰레드들 끼리 상태 변수를 공유하지 말라
상태 변수를 불변하게 만들거나
상태 변수에 접근 할 때는 언제나 synchronization을 사용해라
designing thread-safe classes, good object-oriented techniques - encapsulation, and clear specification of invariants - are your best friend
where the rule is odd remember "first to make your code right,
and then make if fast"
규칙이 잘 맞지 않을때는 일단 잘돌아가게 만들고 빠르게 만들어라
(동시성 버그는 찾기 힘들고 재현하기 힘들기 때문에 작은량의 퍼포먼스 차이라면 포기하는것도 좋다)
2.1 What is Thread safety?
a class is thread-safe when it continues to behave correctly when accessed from multiple threads
쓰레드 세이프티란 여러개의 쓰레드에서 접근해도 정확하게(의도한대로) 지속적으로 동작하는걸 말한다.
a class is thread-safe if it behave correctly when accessed from multiple threads, regardless of the scheduling or interleaving of the execution of those threads by the runtime environment, and with no additional synchronization or other coordination on the part of the calling code
쓰레드 세이프리란 여러개의 멀티 쓰레드에서 접근할때 정확하게 동작하는걸 말한다.
런타임 상테에서 쓰레드의 스케쥴 또는 interleaving 되면서 실행되는거에 상관없어햔다
또한 호출하는 코드에서 동기화나 다른 조정을 할필요가 없어야 한다.
thread safe class encapsulate any needed synchronization so that clients need not provide their own.
쓰레드 세이프 클래스는 모든 동기화를 encapsulate 해야한다 그러므로 클라이언트가 동기화를 위해 다른 코드를 필요로 하면 안된다.
2.1.1 example:A Stateless Servlet
@TreadSafe
public class StatelessFactorizer implements Servlet{
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extracFromRequest(req);
BigInteger[] factors = factor(i);
encodeIntoResponse(resp, factors);
}
}
Stateless objects are always thread-safe
저장 하는 상태가 없으면 쓰레드에 대한 걱정을 하지 않아도 된다.
2.2 Atomicity
read-modify-operation
1.fetch the current value
2.add one to it
3.write the new value back
for example ++count
bad code
@NotThreadSafe
public class UnsafeCountingFactorizer implements Servlet {
private long count = 0;
public long getCount() { return count; }
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = factor(i);
++count;
encodeIntoResponse(resp, factors);
}
}
if the counter is initially 9, with some unlucky timing each thread could read the value
, see that is it 9, add one to it, and each set the counter to 10
9였다가 어떤 나쁜 순간에 쓰레드 두개가 동시에 9인걸 읽는다
그리고 1을 더해서 돌려 놓는다.
위와 같은 예제를 race condition 이라고 한다.
2.2.1 Race Conditions
A race condition occurs when the correctness of a computation depends on the relative timing or interleaving of multiple threads by the run times;
in other words, when getting the right answer relies on lucky timing
the most common type of race condition is check-then-act, where a potentially stale
observation is used to make a decision on what to do next
레이스 컨티션은 정확한 계산이 런타임에서 멀티플 쓰레드의 타이밍과 인터리빙에
의존해서 이루어 지는걸 말한다
다른말로는 정확한 답을 얻기 위해서는 운좋은 타이밍에 의존한다는것이다.
가장 일반적인 레이스 컨디션은 check-then-act 이다
신선하지 않은 정보(최신 데이터가 아닌)를 보고 다음에 무엇을할지 결정하는것이다.
for example race condition
you observe something to be true(file x doesn't exist) and then take action based on
that observation(create x); but in fact the observation could have become invalid
between the time you observed it and the time you act is(someone else created X in
the meantime), causing a problem(unexpected exception..)
2.2.2Example: Race Condition in lazy initialization
badCode
@NotThreadSafe
public class LazyInitRace {
private ExpensiveObject instance = null;
public ExpensiveObject getInstance() {
if (instance == null)
instance = new ExpensiveObject();
return instance;
}
}
Say that thread A and B execute getInstance at the same time.
A see that instance is null, and instantiates a new object. B also checks if instance is
null. whether instance is null at this point depends unpredictably on timing, including the
vagaries of scheduling and how long A takes to instantiate the ExpensiveObject and set
the instance field. if instance is null when B examines it, the two callers to getInstance
may receive two different results, even though getInstance is always supposed to return
the same instance.
A 그리고 B가 동시에 getInstance 를 호출 하면 null 이 나올수 있다
이때 A가만들고 B가 만들면 언제나 하나의 인스턴스만 호출되기를 바랬지만
서로 다른 인스턴스를 돌려 줄수있다
만약 등록하는 객체 같은걸 리턴하는 오브젝트면 다른 오브젝트 등록을 할 수 있다
2.2.3 Compound Actions
Operation A and B are atomic with respect each other if, from the perspective of a thread executing A, when another tread execute B, either all of B has executed or none of it has.
an atomic operation is one that is atomic with respect to all operation, including itself, that operation on the same state
A, B operation 이 원자적이라는 것은 서로 존중 하는걸 의미한다.
B 가 실행되고 있을때 A 를 실행 시킨다면 B 는 전부 실행 되거나
아예 실행되지 않아야 한다. 원자적 operation 이란 하나의 상태에 대해서 자신을 포함한 모든operations 가 존중 받는걸 의미한다.(서로 침범하지 아니함)
what is compound actions?
we refer collectively to check-then-act and read-modify-write sequences as compound actions;
@ThreadSafe
public class CountingFactorizer implements Servlet {
private final AtomicLong count = new AtomicLong(0);
public long getCount() { return count.get(); }
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = factor(i);
count.incrementAndGet();
encodeIntoResponse(resp, factors);
}
}
where practical, use existing thread-safe objects, like AtomicLong, to mange your class's state. it it simpler to reason about the possible states and state transitions for existing thread-safe objects than it it for arbitrary state variables, and this make it easier to maintain and verify thread safety
실전에서는 제공되는 쓰레드 세이프 오브젝트를 쓰는게 유리하다 왜냐하면 가능한 상태값과
상태의 이행 과정을 알수 있기 때문이다 직적 만들면 제 멋대로인 상태 변수들을 관리해야 한다. 즉 이미 존재한걸 쓰는게 쓰레드 세이프를 검증하기도 쉽고 유지하기도 쉽다.
2.3 Locking
아까 서블렛에 캐쉬를 달자
연속되게 같은 값이 들어온다면 캐쉬된 인수 분해 값을 리턴해주는것
(물론 이방법이 비록 효과적이지 않지만)
badCode
@NotThreadSafe
public class UnsafeCachingFactorizer implements Servlet {
private final AtomicReference lastNumber = new AtomicReference();
private final AtomicReference lastFactors = new AtomicReference();
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
if (i.equals(lastNumber.get()))
encodeIntoResponse(resp, lastFactors.get() );
else {
BigInteger[] factors = factor(i);
lastNumber.set(i);
lastFactors.set(factors);
encodeIntoResponse(resp, factors);
 }
}
}
the definition of thread safety requires that invariants be preserved of timing or interleaving of operation in multiple thread
쓰레드 정의를 보면 멀티쓰레드 환경에서 타이밍과 잠시멈춤에도 불변해야 한다
잠시 멈춤을 기억하자!
위코드는 레스트 팩터를 바꿔는대 그때 다른쓰레드가 레스트 팩터를 실어 보내면 오류가 발
생한다.
To preserve state consistency, update related stated variables in a single atomic operation.
상태의 정합성을 유지하기 위해서는 서로 관련이 있는 상태 변수들을 한번의 원자적 operation 으로 처리해야 한다.
2.3.1 Intrinsic Locks
synchronized block has two pars: a reference to an object that will serve as the lock
and a block of code to be guard by that lock;
synchronized block 은 두개의 파트로 구성되어 있다 1. 락자체의 역활을할 오브젝트
그리고 그락이 보호하는코드 블락
synchronized (lock) {
// Access or modify shared state guarded by lock
}
1이락을 intrinsic lock 또는 monitor lock 이라고 부른다.
the only way to acquire an intrinsic lock is to enter a synchronized block or method guard by that lock
내장락을 얻기 위해서는 해당 블락 또는 그락이 막고 있는 메서드에 들어가는 방법밖에 없다.
intrinsic lock act as mutax(or mutual exclusion locks)
2.3.2 Reentrancy
intrinsic locks are reentrant , if a thread tries to acquire lock that it already hols, the request succeeds
내장 락은 재입장이 가능하다 만약 락을 가진 스레드가 다리 그락을 획득 할려고 하면 그
요청은 성공한다.
reentrancy mean that locks are acquired on a per-thread rather than per-invocation
재입장은 락의 획득이 invocation 이 아니라 쓰레드당 관리 될다는걸 알수 있다
구현은 JVM 이 쓰레드가 락을 가지면 owner 와 카운터를 올린다. 재요청 하면 하나를 또 올린다. 락을 나가면 카운터를 내린다. 카운터가 0이 되면 락이 풀린다.
public class Widget {
public synchronized void doSomething() {
}
}
public class LoggingWidget extends Widget {
public synchronized void doSomething() {
System.out.println(toString() + ": calling doSomething");
super.doSomething();
}
}
위의 코드를 보면 위젯을 상속해 로깅 위젯을 만들었다
하지만 내장 락이 재입장이 불가능하다면 위와 같은 메서드를 호출할때 데드락이 걸릴것이다.
2.4 Guarding State with locks
For each mutable state variable that may be accessed by more than one thread, all access to that variable must be performed with the same lock held, in this case, we say that the variable is guarded by that lock.
각각의 변경 가능한 상태의 변수들이 만약 한개이상의 쓰레드에서 접근된다면
그 변수에 접근하는 모든 락은 동일한 락으로 방어되어야한다.
이와같은 상황에 우리는 그 변수는 락에의해 방어 되고 있다고 할 수 있다.
every shared, variable should be guarded by exactly one lock. make it clear to maintainers which lock that is
모든 공유 변수들은 단 한하나의 락으로 방어 되어야한다. 그래서 유지 보수자가 어떤락이
그 역활을 하는지 쉽게 알 수 있어야 한다.
For every invariant that involve more than one variable, all the variables involved in that invariant must be guarded by the same lock
불변성에 하나 이상의 변수가 연관된다면 연관된 모든 변수들은 반드시 하나의 락으로
관리 되어야 한다.
liveness concerns the complementary goal that "something good
eventually happens".
2.5 Liveness and Performance
@ThreadSafe
public class CachedFactorizer implements Servlet {
@GuardedBy("this")
private BigInteger lastNumber;
@GuardedBy("this")
private BigInteger[] lastFactors;
@GuardedBy("this")
private long hits;
@GuardedBy("this")
private long cacheHits;
public synchronized long getHits() {
return hits;
}
public synchronized double getCacheHitRatio() {
return (double) cacheHits / (double) hits;
}
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = null;
synchronized (this) {
++hits;
if (i.equals(lastNumber)) {
++cacheHits;
factors = lastFactors.clone();
}
}
if (factors == null) {
factors = factor(i);
synchronized (this) {
lastNumber = i;
lastFactors = factors.clone();
}
}
encodeIntoResponse(resp, factors);
}
}
there is frequently a tension between simplicity and performance. when implementing synchronization policy, resist the temptation to prematurely sacrifice simplicity for the shake of performance
명료함과 성능 사이에서 고민할때가 많다 동기화 정책을 구현할때는 성능을 위해 너무 빨리
명료함을 버리려는 유혹에 빠지지 말자
avoid holing locks during lengthy computation or operations at risk of not completing quickly such as network or console I/O
일찍 끝나지 않는 network 또는 IO 를 하는 오퍼레이션 또는 긴계산 시간이 필요한 코드들은
락을 걸때 피하자
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 을 사용해야한다.
해당 예제는 리더쓰레드에서 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 값을 볼수도 있다는거다
위에 꺼는 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을 보는걸 확신 하기 위해 사용하자(즉 확인 용이지 락이나 메모리 보임을 보장하기
위한 용도로 쓰지 말자)
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 라고 한다.
예제
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 메서드에서 오브젝트를 돌려주는것도 같다.
위와 같은 상황에서는 어떤 콜러도 배열의 값을 변경 할 수 있으로 해당 상태 배열은 의도된 scope 에서 escaped 되었다고 할 수 있다
위와 같은 상황에서는 this 가 같이 퍼블리시 되었다 문제는 ThisEscape 가 아직 완전이
컨스트럭트 된게 아니다.
예를 들어보자 source에 이벤트 리스트 인스턴트 등록한다.
이제 외부에서 이벤트 발행하면 해당 이벤트
리스너가 듣게 되는대 문제는 이 이벤트 리스너가 컨스트럭터의 맨 아래 부분의 initSomeValue()에서 set 하는 변수를 사용한다고 하면
ThisEscpe 가 do a lot of thing 를 하는 동안 실행 될 수 있다
즉 컨스트럭터에서 외부에서 완전이 만들어지지 않은 this 를 사용 할 수 있게 배포한것이다.
(쓰레드를 컨스트럭터에서 실행 할때 위 와 같은 상황이 많이 발생된다.)
만약 생성자에서 쓰레드를 생성한 후 시작 시키거나 이너 클래스를 등록하고 싶으면 위와 같은 방법으로 공개적인 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
불변 홀더를 사용한다고 해보자 만약 쓰레드가 해당 홀더의 레퍼런스를 같는다면
다른 쓰레드가 홀더의 상태를 변경하는걸 걱정하지 않아도 된다
왜냐하면 상태를 변경하기 위해서는 홀더 자체를 새로 생성 해야 하기 때문이다
또한 생성한다고 해도 이전홀더를 보고 있던해들은 이전 홀더를 보기 때문에 일관된 상태를
볼수 있다( 원자적으로 변경되어야 되는 변수들이 동시에 움직인다.)
2.copy를 사용했기 때문에 내부 상태를 변경 할 수 없다.
3.코드를 로직에서 한번만 참조하기 때문에 문제가 생길일이 없다.
즉 락을 걸지 않아도 thread-safe 하게 할 수 있다
맨아래의 예제는 컨스트럭션은 잘되었지만 volatile 이아니기 때문에 문제가 발생된다.
위와 같이 된다면 다른 쓰레드에서 부를때 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 락에의해 보호 되서 해당 쓰레드에 접근하기 위해서는 락을 소유해야함
동기화는 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 SetknownSecrets; 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 락에의해 보호 되서 해당 쓰레드에 접근하기 위해서는 락을 소유해야함
2013년 12월 27일 금요일
java concurrency in practice ch1
ch1 - introduction
멀티 쓰레드 프로그램은 복잡하다 근대 왜 써야 하는가?
멀티 프로세서에서 가장 쉽게 확장 할 수 있음으로
1.1 brief history of concurrency
in the acient past,
running only a single program at a time was an inefficient use of expensive and scarce computer resource
os evolved and run more than one program to run at once
running individual programs in process
if they need to, processes can communicate with one another
Resouce utilization
it it more efficient to use that wait time to let another program run
Fairness
let them(users, processes) share the computer via finger-grained time slicing
Convenience
wrtie serverel programs that each perform a single task and have them coordinate wite each other
"virtual von Neumann computer it had a memory space storing both instructions and data
, executing instructions sequentially" mean sequential programming model
Threads allow multiple streams of program control flow to coexist within a process
thread share process-wide resource such as memory and file handler
thread not share program counter, stack, local valiables
multiple threads within the same program can be scheduled simultaneouly on multiple CPUs
most moderan os treat threads as the basic unit of scheduling
thread share memory so it allows finer-grained data sharing than inter-process
but modify variables that another thread is in the middle of using, with unpreditable results
프로세스 는 여러개의 쓰레드로 이루어질수 있음
쓰레드는 프로세스의 메모리공간 공유
쓰레드의 스텍, 로컬 변수, 프로그램 카운터는 따로씀
하나의 프로그램을 쉽게 여러개의 코드로 분리 한 후 서로 협동하게 만들 수 있음
또한 멀티 cpu에도 쉽게 배분할 수 있음
문제는.. 쓰레드 관리가 힘들다는거!
1.2 Benefits of Threads
thread make it easier to program, by turing asynchronous workflow into mostly sequetial ones
1.2.1 Exploiting Multiple Processors
single thread program run at just one processor if 100 cpus than 99% of processor remains idels
1.2.3. simplicity of modeling
it is better assinging a thread to each type of task than manaing multiple different types of task at once
thread is used by framewoks such as servlets or RMI(servlet writers do not need to worry about mulitple user connections)
1.2.3 simplified Handling of Asynchronous Events
1.2.4 More Responsive User Interfaces
if long-running taks is executed in a serperate thread , the event thread remains free to prcess UI events, making thre UI more responsive
장점
1.여러개의 코어 사용가능
2.하나의 쓰레드만 노출하고 그 쓰레드와 작동하는 다른 쓰레드들을 감춤으로서 쉽게 프로그램 개발가능
(framework 개발에사용)
복잡한 일을 몇개의 쓰레드로 분리해서 작성 후 서로 연동하게함
3.비동기 이벤트 다루는대 좋음
nio
4.UI에서 오래 걸리는 일을 다른 쓰레드에서 실행 시킴으로서 유저의 입력에 대한 반응을 계속 할수 있음
1.3 Risks of Thread
thread is more esoteric, concurrency was an "advancded" topic
1.3.1 Safety Hazards
멀티 쓰레드 프로그램은 복잡하다 근대 왜 써야 하는가?
멀티 프로세서에서 가장 쉽게 확장 할 수 있음으로
1.1 brief history of concurrency
in the acient past,
running only a single program at a time was an inefficient use of expensive and scarce computer resource
os evolved and run more than one program to run at once
running individual programs in process
if they need to, processes can communicate with one another
Resouce utilization
it it more efficient to use that wait time to let another program run
Fairness
let them(users, processes) share the computer via finger-grained time slicing
Convenience
wrtie serverel programs that each perform a single task and have them coordinate wite each other
"virtual von Neumann computer it had a memory space storing both instructions and data
, executing instructions sequentially" mean sequential programming model
Threads allow multiple streams of program control flow to coexist within a process
thread share process-wide resource such as memory and file handler
thread not share program counter, stack, local valiables
multiple threads within the same program can be scheduled simultaneouly on multiple CPUs
most moderan os treat threads as the basic unit of scheduling
thread share memory so it allows finer-grained data sharing than inter-process
but modify variables that another thread is in the middle of using, with unpreditable results
프로세스 는 여러개의 쓰레드로 이루어질수 있음
쓰레드는 프로세스의 메모리공간 공유
쓰레드의 스텍, 로컬 변수, 프로그램 카운터는 따로씀
하나의 프로그램을 쉽게 여러개의 코드로 분리 한 후 서로 협동하게 만들 수 있음
또한 멀티 cpu에도 쉽게 배분할 수 있음
문제는.. 쓰레드 관리가 힘들다는거!
1.2 Benefits of Threads
thread make it easier to program, by turing asynchronous workflow into mostly sequetial ones
1.2.1 Exploiting Multiple Processors
single thread program run at just one processor if 100 cpus than 99% of processor remains idels
1.2.3. simplicity of modeling
it is better assinging a thread to each type of task than manaing multiple different types of task at once
thread is used by framewoks such as servlets or RMI(servlet writers do not need to worry about mulitple user connections)
1.2.3 simplified Handling of Asynchronous Events
1.2.4 More Responsive User Interfaces
if long-running taks is executed in a serperate thread , the event thread remains free to prcess UI events, making thre UI more responsive
장점
1.여러개의 코어 사용가능
2.하나의 쓰레드만 노출하고 그 쓰레드와 작동하는 다른 쓰레드들을 감춤으로서 쉽게 프로그램 개발가능
(framework 개발에사용)
복잡한 일을 몇개의 쓰레드로 분리해서 작성 후 서로 연동하게함
3.비동기 이벤트 다루는대 좋음
nio
4.UI에서 오래 걸리는 일을 다른 쓰레드에서 실행 시킴으로서 유저의 입력에 대한 반응을 계속 할수 있음
1.3 Risks of Thread
thread is more esoteric, concurrency was an "advancded" topic
1.3.1 Safety Hazards
피드 구독하기:
글 (Atom)