2. 목차
I. Java Concurrency Programming 101
II. 동기화
III. Executor 프레임워크
IV. 더 알아보아야 할 내용들
V. Q & A
VI. References
THE SYS4U PAPER 2012 | 2
3. I. Java Concurrency Programming 101
1. 왜 병렬 처리를 해야 하는가?
2. Thread/Runnable
3. Thread 클래스의 Method들
4. ThreadLocal 이란?
5. Wait – Notify / NotifyAll
6. Timeout Job 예제 – 고전적인 기법
THE SYS4U PAPER 2012 | 3
4. I. Java Concurrency Programming 101
1. 왜 병렬 처리를 하는가?
Good to better, better to best.
누구도 그저 작동하기만 하는 어플리케이션을 원하지 않는다.
Serial Process Parallel Process
성능 향상
- 오래 걸리는 하위 작업으로 인해 나머지 모든 작업들이 지연되는 것을 막을 수 있다.
자원 사용 효율 증대
- 시스템 자원(멀티코어 프로세서, 메모리, IO장치 등) 사용율을 높여 결과적으로 어플리케이션의 성능을 향상시킬 수 있다.
프로그램의 단순화
- (잘 작성할 수만 있다면) 여러 가지 작은 작업을 분할해서 수행하는 것이 한번에 큰 작업을 수행하는 것 보다 쉽다.
THE SYS4U PAPER 2012 | 4
5. I. Java Concurrency Programming 101
2. Thread / Runnable
Java Concurrent ABC
java.lang.Thread 클래스를 이용하면 실제 OS의 Thread와 연결되는 Heavy-Weight Thread 객체를 생성할 수 있다.*
Starting main-thread job
Finishing main-thread job
Starting user-thread job
---- after 1 second……
Finishing user-thread job
THE SYS4U PAPER 2012 | 5
6. I. Java Concurrency Programming 101
3. Thread 클래스의 메소드들
Managing Threads
Multi-Thread 운용을 위한 기본적인 모든 기능을 제공한다.
setName(String name)
- Thread에 이름을 붙인다. 설정하지 않은 경우 기
본 이름(Thread-#)으로 설정된다.
setDaemon(boolean on)
- Thread를 Daemon Thread로 만들지 여부를 결
정한다. Main Thread는 Daemon Thread만 남아
있으면 종료된다.
setUncaughtExceptionHandler
- RuntimeException이 발생한 경우 처리할 클래
스를 설정한다.
start
- 재정의된 run 메소드를 병렬 실행한다. 만일 run
메소드를 직접 실행하면 Main Thread에서 실행되
므로 병렬처리 효과가 발생하지 않는다.
Join / join(long milliseconds)
- 이 Thread가 종료될 때 까지 대기한다.
THE SYS4U PAPER 2012 | 6
7. I. Java Concurrency Programming 101
4. ThreadLocal이란?
Oh, my precious.
각각의 Thread들은 공유되지 않는 자신만의 저장 공간이 필요하다.
ThreadLocal의 장점
- stateful 클래스가 별도의 동기화 작업 없이 Thread-Safe하게 처리 가능해진다.
- 동기화에 대한 복잡한 설계나 정의가 필요없이 non-Thread-Safe 객체에 대한 처리가 가능하다.
- 동기화 과정이 필요가 없기 때문에 성능의 향상을 기대할 수 있다.
ThreadLocal의 주의사항
- ThreadPool을 이용할 경우 Thread가 재사용될 수 있으므로, ThreadLocal 변수도 반드시 초기화하거나 무효화하여야 한다.
THE SYS4U PAPER 2012 | 7
8. I. Java Concurrency Programming 101
5. Wait – Notify / NotifyAll
Don’t bother me, I’ll let you know.
Object 클래스에 final로 선언되어 있는 wait/notify/notifyAll 메소드를 이용하면 효율적으로 Thread의 동작을 제어할 수 있다.
wait 메소드는 코드 블럭을 사용하려는 모든 Thread를 대기하게 하고, notify/notifyAll 메소드는 대기하고 있던 Thread가 실행되도록 한다.
notify는 가장 먼저 대기한 Thread에게 작업 소유권을 부여하고, notifyAll은 우선순위가 가장 높은 Thread에게 작업 소유권을 부여한다.
Wait… I’m not ready. Ok. Let’s do it.
Wait… I’m not ready.
Can I work with you? Yeah~ Rock’n Roll!
Can I work with you? Still waiting……
THE SYS4U PAPER 2012 | 8
9. I. Java Concurrency Programming 101
6. Timeout Job 예제 – 고전적인 기법
Join me
별개의 Thread를 이용하여 메소드 실행이 일정 시간을 넘지 않도록 할 수 있다.
실제 수행할 작업을 Anonymous
Runnable 인스턴스를 이용하여 처리
하도록 하고 Thread를 시작한다.
최대 실행 시각만큼 join한 뒤에 결과
를 리턴한다.
고전적인 Timeout 기법
- 작업을 수행할 별도 Thread를 이용
- Main Thread(혹은 작업 Thread를 실행한 Thread)는 작업 Thread에 join하여 작업 Thread의 종료를 기다림
- 필요한 경우 또다른 Thread를 이용하여 명시적으로 작업 Thread를 취소할 수 있음
THE SYS4U PAPER 2012 | 9
10. II. 동기화
1. 왜 동기화를 해야 하는가?
2. 동기화의 조건들
3. Atomic Wrapper Classes
4. volatile Keyword
5. synchronized Keyword
6. synchronized Keyword(2)
7. 명시적 동기화 - Lock / ReentrantLock
THE SYS4U PAPER 2012 | 10
11. II. 동기화
1. 왜 동기화를 해야 하는가?
Tragedy of Commons.
여러 개의 Thread가 동시에 동일한 자원에 접근할 경우에 해당 자원에 대한 접근을 제한하여 안정성을 보장하여야 한다.
공유자원이 제대로 관리되지 않을 경우 비극적인 일이 일어난다.
하지만, 역으로, Thread간에 공유할 자원이 없다면 동기화를 할 필요가 없다는 의미도 된다.
UnsafeSequence.java
I feel weird…
Timeline
Thread value -> 0 0 + 1 -> 1 value = 1
A
Thread
value -> 0 0 + 1 -> 1 value = 1
B
Rock’n Roll!
Rock’n Roll!
THE SYS4U PAPER 2012 | 11
12. II. 동기화
2. 동기화의 조건들
All for One, One for All
완벽한 동기화가 이루어지기 위해서는 원자성, 가시성, 코드 재배치 방지가 모두 보장되어야 한다..
Atomicity Visibility Code Reorder
THE SYS4U PAPER 2012 | 12
13. II. 동기화
3. Atomic Wrapper Classes
Synchronize without synchronized
java.util.concurrent.atomic 패키지에서 제공되는 Atomic* 클래스들은 별도의 동기화 처리가 필요치 않은 동기화 메소드들을 제공한다.
별도의 동기화 작업이 필요치 않다는 점에서 성능 향상을 도모할 수 있지만,
두 개 이상의 Atomic 객체를 사용하는 상황에서는 별도의 동기화 처리가 필요하다.
OK, Rock’n Roll! I feel weird…
THE SYS4U PAPER 2012 | 13
14. II. 동기화
4. volatile 키워드
내가 너 그럴 줄 알았다.
완벽한 동기화는 가시성, 원자성, 코드 재배치라는 세 가지 요소에 대해 안전하게 처리할 것을 보장해야한다.
volatile 키워드는 이들 중 가시성과 코드 재배치에 대한 안전한 처리를 보장한다.
원자성이 보장되어야 할 필요가 없다면 volatile 키워드로 충분하다.
OK, Rock’n Roll! I feel weird…
THE SYS4U PAPER 2012 | 14
15. II. 동기화
5. synchronized Keyword
Perfection requires the hand of time
synchronized 키워드는 원자성, 가시성, 코드 재배치 모두를 제어하는 완벽한 동기화 처리를 지원하지만, 완벽한 만큼 더 많은 시간을 필요
로 한다.
synchronized 키워드는 메소드 선언부에 사용하여 this 인스턴스에 대해 동기화 할 수 있으며, 이를 암묵적 잠금(implicit lock)이라고 한다.
THE SYS4U PAPER 2012 | 15
16. II. 동기화
6. synchronized Keyword(2)
최소 적용으로 최대 효과
synchronized 블럭을 이용하면 더 명시적이고 효과적으로 필요한 부분에 대해서만 동기화를 수행할 수 있다.
필요할 경우 외부에서 접근할 수 없는 private의 잠금용 인스턴스를 이용하여 동기화를 수행할 수도 있다.
THE SYS4U PAPER 2012 | 16
17. II. 동기화
7. 명시적 동기화 – Lock / ReentrantLock
Everyone is mad but some people can control the madness.
JSDK1.4까지는 synchronized 키워드와 volatile 키워드만 사용할 수 있었다.(그나마 volatile은 5.0부터 제대로 동작하게되었다.)
JSDK5.0에서 제공되는 Lock 인터페이스는 기존의 방식보다 더 명시적이면서 기존에는 사용하지 못했던 고급 기능을 사용할 수 있게 해 준다.
Unconditional Lock
- 좌측의 예제에서와 같이 특별한 조건을 걸지 않
는 경우이다. 기본적으로 synchronized 블럭과 동
일하게 동작한다.
Lock 구현체를 이용하는 모든 코드에서는 반드시
try-finally 구문을 이용하여 Lock.unlock()을 수행
하여야 한다.
Timeout Lock
- Lock.tryLock(long timeout, TimeUnit unit) 메
소드를 이용하면 일정 시간동안만 lock을 획득하
기 위해 대기할 수 있다.
Interruptible Lock
- Lock.lockInterruptibly() 메소드를 이용하면
lock이 외부의 interrupt() 호출에 반응하도록 할
수 있다.
THE SYS4U PAPER 2012 | 17
18. III. Executor 프레임워크
1. 왜 Thread의 수를 제한해야 하는가?
2. Executor Interface
3. Callable / Future
4. 진보된 Timeout Job 예제
THE SYS4U PAPER 2012 | 18
19. III. Executor 프레임워크
1. 왜 Thread의 수를 제한해야 하는가?
덮어놓고 낳다보면 거지꼴을 못 면한다.
JVM이 사용할 수 있는 자원은 한정되어 있다.
필요하다고 그 때 그 때 만들어 사용하면 여러 가지 문제를 겪을 수 있다.
Thread Life-Cycle 문제
- Thread가 Process보다 경량이긴 하지만, 생성하고 제거하는 데에
적지 않은 자원이 소모된다. 너무 많은 Thread를 생성하게 되면 생
성된 Thread를 관리하는 데에 많은 비용이 든다.
Memory 문제
- (OS마다 다르지만) 보통 Thread 하나를 생성하여 관리하는 데에
256KB~512KB 가량의 메모리가 소비된다. 과도하게 Thread를 생
성할 경우 어플리케이션이 사용할 수 있는 메모리의 양이 제한될 수
있다.
자원의 과도한 사용
- 모든 OS에는 자원(Memory, File Handle, Thread 수) 사용 제한이
있다. 또, JVM의 실행 설정값에 따라 Java 어플리케이션이 이용할
수 있는 자원의 크기도 제한된다. 만약 Java 어플리케이션이 실행되
는 과정에서 자원 제한값을 초과하여 사용하게 되면
OutOfMemoryError가 발생할 수 있으며 이런 종류의 문제는 해결
이 불가능하다.(어플리케이션을 재시작하는 것이 바람직하다.)
OutOfMemoryError가 발생하지 않는다 하더라도, 제한 없이
Thread를 생성하는 것은 성능에 악영향을 끼칠 수 있다.
THE SYS4U PAPER 2012 | 19
20. III. Executor 프레임워크
2. Executor Interface
The Newtype Executor
java.util.concurrent.Executor 인터페이스를 이용하면 Thread를 직접 이용하여 병렬처리하는 것보다 훨씬 많은 이익을 얻을 수 있다.
과거 new Thread(Runnable).start();를 이용했던 코드는 모두 Executor를 이용하도록 변경하는 것이 바람직할 정도로 이제는 기본 병렬처
리 기법이라고 말할 수 있다.
Executors.newFixedThreadPool
- 고정된 크기의 ThreadPool을 생성한다. 지정된 크기 이상
의 Thread를 할당하는 것이 불가능하다.
Executors.newCachedThreadPool
- 기본적으로는 fixed size ThreadPool과 동일하다. 하지만
만약 지정된 크기보다 더 많은 수의 Thread가 필요할 경우
임시로 Thread를 만들어 사용한 뒤 해당 Thread를 제거한다.
(고정된 크기가 증가되지는 않는다.)
Executors.newSingleThreadExecutor
- 하나의 Thread만 갖는 ThreadPool을 생성한다. 작업이 순
차적(FIFO, LIFO 등)으로 실행되어야 하는 경우에 사용된다.
Executors.newScheduledThreadPool
- 기본적으로 fixed size ThreadPool이며, 병렬처리 작업이
지연되거나 특정 시간에 실행될 수 있도록 한다.
THE SYS4U PAPER 2012 | 20
21. III. Executor 프레임워크
3. Callable / Future
나는 앞으로 일어날 일을 알고 있다.
JSDK5.0부터 제공되는 Callable/Future 구조를 이용하면 병렬처리 작업으로부터 직접 결과를 얻어 사용할 수 있다.
과거에는 병렬처리 작업으로부터 결과를 얻거나 Exception을 전달받기 위해 복잡한 작업을 수행해야 했으나, 이제는 옛 일이 되어 버렸다.
THE SYS4U PAPER 2012 | 21
22. III. Executor 프레임워크
4. 진보된 Cancellation 예제
The wise man avoids evil by anticipating it.
Callable과 Future를 이용하면 병렬처리 작업을 손쉽게 취소할 수 있다.
THE SYS4U PAPER 2012 | 22
23. IV. 더 알아보아야 할 내용들
1. Thread 관련
2. 동기화 관련
3. java.util.concurrent 패키지 관련
THE SYS4U PAPER 2012 | 23
24. IV. 더 알아보아야 할 내용
1. Thread 관련
It was the tip of the iceberg.
Thread의 개략적인 사용법 뿐만 아니라 세부적인 개념을 이해할 수 있어야 한다.
Thread Life-Cycle
- Thread가 태어난 뒤 어떤 생을 살게 될까?
interruption
- Thread를 멈추는 방법은 무엇인가? 멈추라고 하면 정말 멈출까? 멈추기 위해 어떤 작업을 해야 하나?
Deprecated methods
- stop, suspend와 같은 메소드들은 왜 deprecated되었을까?
wait – notify / notifyAll
- 함부러 기다리라고 했다가는 큰일난다.
Advanced Callable / Future
- 미래를 지배하는자!
THE SYS4U PAPER 2012 | 24
25. IV. 더 알아보아야 할 내용
2. 동기화 관련
Welcome to hell.
Lock을 거는 순간 모든 문제가 발생하기 시작한다.
성능 문제
- 과도한 동기화가 성능에 어떤 영향을 미칠까? 그럼 적절한 동기화는 괜찮나?
데드락(Dead-Lock)
- 서로 다른 두 개의 Thread가 각각 상대방의 작업이 종료되기를 기다린다면 어떻게 될까?
자원 고갈(Starvation)
- 하나의 Thread가 공유자원을 모두 소비해 버린다면?
객체 공개(Object Publication)
- 인스턴스를 잘못된 방식으로 외부에 공개하는 것은 동기화 구조를 깰 수 있다.
재진입성(Reentrancy)
- synchronized로 보호되어 있는 동기화 메소드가 자기 자신을 재귀호출한다면 어떻게 될까?
Synchronized Collections
- Vector나 Hashtable을 더 이상 사용하지 않는 이유는?
ConcurrentModificationException
- Iterator에 대한 작업을 수행하고 있는 과정에서 Collection의 내용이 변경된다면 어떠한 일이 발생할까?
THE SYS4U PAPER 2012 | 25
26. IV. 더 알아보아야 할 내용
3. java.util.concurrent 패키지 관련
Abstraction is Simplification.
Java Concurrent Library는 과거 직접 만들어 사용해야 했던 수많은 병렬처리/동기화 개념들을 멋지게 추상화하여 사용하기 쉽게 만들었다.
ExecutorService Life-Cycle
- ExecutorService의 시작, 실행, 종료, 강제종료, 제거 등은 어떻게 처리되는가?
ThreadPool Sizing
- ThreadPool의 크기를 얼마로 할 때 최적의 성능을 발휘할 수 있는가?
CountDownLatch
- n개의 Thread가 처리완료될 때까지 기다리고 싶다면?
CyclicBarrier
- 특정한 조건이 만족될 때까지 Thread들의 동작을 대기하고 싶다면?
Semaphore
- 특정한 자원에 대해 접근이 허용되는 수 n 보다 더 많은 수 m개의 Thread가 동작하고 있다면?
BlockingQueue
- ExecutorService를 이용하여 손쉽게 Producer-Consumer 패턴을 구현하고 싶다면?
Fork Join Framework
- Callable 내에서 재귀 호출을 하는 것 같이 미래에 일어날 일을 완전히 예측하지 못하는 경우에는 Future를 사용할 수 없을까?
THE SYS4U PAPER 2012 | 26
28. VI. References
Java Concurrency in Practice. - Brian Goetz 외 5명(Addison Wesley)
5 things you didn’t know about java.util.concurrent. - Ted Neward(IBM DeveloperWorks)
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html
http://www.journaldev.com/1090/java-callable-future-example - Pankaj(JournalDev)
THE SYS4U PAPER 2012 | 28