Chapter 9. Thread
1. Process & Thread
- Process
· 실행 중인 Program을 지칭하며 Program을 실행 하면 OS로부터 실행에 필요한 자원을 할당받아 Process가 된다.
· Process는 Program을 수행하는데 필요한 Data, Memory, Resource, Thread로 구성된다.
· Process 내에는 최소한 하나 이상의 Thread가 존재한다.
· Process의 Memory 한계(Call Stack의 크기)에 따라 Process 내에 생성할 수 있는 Thread의 수가 결정된다.
· Multi-Threaded Process
◦ Process 내에 둘 이상의 Thread가 존재하는 Process
◦ 여러 Thread가 하나의 Process 내에서 자원을 공유하면서 작업하기 때문에 문제가 발생할 수 있다.
◦ 장점
▹ CPU의 사용률을 향상시킨다.
▹ 자원을 보다 효율적으로 사용할 수 있다.
▹ 사용자에 대한 응답성이 향상된다.
▹ 작업이 분리되어 Code가 간결해진다.
◦ 단점
▹ 동기화(Synchronization), 교착상태(Deadlock) 등 자원 공유 문제가 발생할 수 있다.
2. Thread
- Process 내의 실행 흐름
- Process 내의 Code, Data를 이용해 Program의 실행을 관리한다.
- 특징
· Java에서는 Main Method가 실행되면 하나의 Process가 작업을 진행하며 해당 Thread를 Main Thread라고 한다.
· Main Thread 하나만 작업을 진행하는 것을 Single-Thread라고 한다.
- Thread의 구현 & 실행
· Thread 구현 방법
◦ Thread 구현 방법은 두 가지가 있는데 Thread Class를 상속받거나 Runnable Interface를 구현하는 방법이 있다.
◦ Thread Class
1 2 3 4 5 6 7 8 9 10 11 | public class Thread { private Runnable r; public Thread(Runnable r) { this.r = r; } public void run() { if(r!=null) r.run(); } } | cs |
▹ Runnable을 구현한 Class의 Instance를 참조하기 위한 변수를 선언한다.
▹ Runnable Interface를 구현한 Instance의 run()을 호출해 사용한다.
◦ Thread Class 상속 방법
1 2 3 4 5 | class MyThread extends Thread { public void run { /* 작업 내용 */ } } | cs |
◦ Runnable Interface
1 2 3 | public interface Runnable { public abstract void run(); } | cs |
▹ run() Method만 정의되어있는 간단한 Interface
▹ Runnable Interface 구현할 경우
1 2 | Runnable r = new MyThread(); Thread t = new Thread(r); | cs |
▸ Runnable Interface를 구현한 Class의 Instance를 생성한 후, 해당 Instance를 Thread Class의 Instance를 생성할 때 생성자의 매개변소로써 넣어줘야 한다.
▹ Runnable Interface 구현 방법
1 2 3 4 5 | class MyThread implements Runnable { public void run { /* 작업 내용 */ } } | cs |
- run() & start()
· run()과 start() Method 간의 차이점
◦ run()
▹ 생성된 Thread를 실행시키는 것이 아니고 단순히 해당 Class의 run() Method 하나를 호출하는 것
◦ start()
▹ 새로운 Thread가 작업을 실행하기 위해 필요한 Call Stack을 생성한 후 해당 Stack에 run() Method를 호출한다.
▹ 호출된 run() Method는 Call Stack에 처음으로 등록되기 때문에 생성된 Call Stack에 첫 번째로 저장된다.
· start() Method에 의한 run() Method 호출 순서
◦ main Method에서 Thread의 start Method 호출
◦ start Method는 Thread가 작업을 수행하는데 사용될 새로운 Call Stack 생성
◦ 생성된 Call Stack에 Run() Method를 호출해 Thread가 작업을 수행하도록 함
◦ Call Stack이 두 개가 되었기 때문에 Scheduler가 정한 순서에 의해서 번갈아 가면서 실행
- Single-Thread & Multi-Thread
· Single-Thread
◦ 하나의 Thread로 두 개의 작업을 처리하는 경우
◦ 한 작업을 마친 후 다른 작업을 수행
· Multi-Thread
◦ 두 개의 Thread로 두 개의 작업을 처리하는 경우
◦ 짧은 시간동안 2개의 Thread가 번갈아 가면서 작업을 수행
· Single-Thread & Multi-Thread 비교
◦ 작업 수행 시간을 비교하면 Single-Thread와 Multi-Thread의 작업 수행 시간은 거의 같다.
◦ 오히려 작업 전환(Context Switching) 때문에 두 개의 Thread로 작업한 시간이 Single-Thread로 작업한 시간보다 더 걸린다.
◦ 따라서 단순히 CPU만을 사용한 계산작업이라면 Single-Thread로 작업하는 것이 효율적이다.
· Thread의 특징
◦ Java는 OS(Platform)에 독립적이지만 일부 종속적인 부분도 존재하며 Thread는 이 종속적인 부분에 해당한다.
◦ Multi-Thread의 작업 순서는 OS의 Process Scheduler에 의해 실행할 때마다 다른 결과를 얻게 된다.
- Priority of Thread
· Thread는 우선순위(Priority)라는 Member를 가지고 있으며 이 우선순위의 값에 따라 Thread가 얻는 실행시간이 달라진다.
· 특징
◦ Thread 작업 중요도에 따라 Thread의 우선순위를 서로 다르게 지정할 수 있다.
◦ Thread의 우선순위는 1~10까지 지정할 수 있으며 숫자가 높을수록 우선순위가 높다.
◦ main Method 내의 자동으로 5로 설정된다.
· Thread 우선순위에 의한 작업
◦ Thread의 우선순위를 확인
◦ 우선순위에의해 각 Thread에 실행 시간 할당
◦ 우선순위를 할당 받은 모든 Thread Scheduler에 의해 각각 할당된 실행 시간만큼만 실행하고 전환
- Thread Group
· 서로 관련된 Thread를 Group으로 묶어 관리하기 위한 것
· 특징
◦ Thread Group에는 다른 Thread Group을 포함시킬 수 있다.
◦ 모든 Thread는 반드시 Thread Group에 포함되어 있어야 한다. ◦ Thread Group을 지정하는 생성자를 사용하지 않은 Thread는 기본적으로 자신을 생성한 Thread와 같은 이름을 가진 Thread Group에 속하게 된다.
- Daemon Thread
· 일반 Thread의 작업을 돕는 보조적인 역할을 수행하는 Thread
· 특징
◦ 일반 Thread가 모두 종료되면 Daemon Thread 또한 강제적으로 자동 종료된다.
◦ 무한 Loop와 조건문을 이용해 실행 후 대기하고 있다가 특정 조건이 만족되면 수행하고 다시 대기하도록 작성된다.
◦ 일반 Thread의 작성 방법과 실행 방법이 같으며 Thread를 생성한 후 실행하기 전에 setDaemon(true)를 호출하기만 하면된다.
※ 현재는 suspend() 대신에 wait()를 resume() 대신에 notify()를 사용하고 있다.
- Thread 상태
Status
|
Explain
|
NEW |
Thread가 생성되고 아직 start()가 호출되지 않은 상태 |
RUNNABLE |
실행 중 또는 실행 가능한 상태 |
BLOCKED |
동기화 Block에 의해 일시정지된 상태(lock이 풀릴 때까지 기다리는 상태) |
WAITING, TIMED_WAITING |
Thread의 작업이 종료되지는 않았지만 실행가능하지 않은 일시정지 상태 TIMED_WAITING은 일시정지시간이 지정된 경우를 의미 |
TERMINATED |
Thread의 작업이 종료된 상태 |
- 작업 수행에 따른 Thread 상태
· 단계 1)
◦ Thread가 생성하고 start()를 호출하면 바로 실행되는 것이 아니라 실행대기열에 저장되어 자신의 차례가 될 때까지 기다려야한다.
◦ 실행 대기열은 Queue와 같은 구조로 먼저 실행대기열에 들어온 Thread가 먼저 실행된다.
· 단계 2)
◦ 실행 대기상태에 있다가 자신의 차례가 되면 실행상태가 된다.
· 단계 3)
◦ 주어진 실행시간이 다되거나 yield()를 만나면 다시 실행대기상태가 되고 다음 차례의 Thread가 실행상태가 된다.
· 단계 4)
◦ 실행중 suspend(), sleep(), wait(), join(), I/O block에 의해 일시정지상태가 될 수 있다.
◦ I/O block은 입출력 작업에서 발생하는 지연상태를 말한다.
◦ 사용자의 입력을 기다리는 경우 예로 들 수 있는데, 이런 경우 일시정지 상태에 있다가 사용자가 입력을 마치면 다시 실행대기상태가 된다.
· 단계 5)
◦ 지정된 일시정지시간이 다되거나(time-out), notify(), resume(), interrupt()가 호출되면 일시정지 상태를 벗어나 다시 실행대기열에 저장되어 자신의 차례를 기다리게 된다.
· 단계 6)
◦ 실행을 모두 마치거나 stop()이 호출되면 Thread는 소멸된다.
- Thread 동기화(Synchronization)
· Multi-Thread Programming에서 중요한 요소로 얼마나 동기화를 잘 처리하는가에 따라서 프로그램의 성능에 많은 영향을 미친다.
· synchronized를 이용한 동기화
◦ 작업과 관련된 공유 Data에 lock을 걸어서 먼저 작업 중이던 Thread가 작업을 완전히 마칠 때까지는 다른 Thread에게 제어권이 넘어가더라도 Data가 변경되지 않도록 보호함으로써 Thread 동기화를 가능하게한다.
◦ synchronized를 이용하면 Thread 동기화에는 유용하지만 교착상태(Deadlock)에 빠져 Program이 정상적으로 작동하지 않을 수 있다.
◦ synchronized block
▹ synchronized block에서 지정된 객체는 synchronized block의 시작부터 lock이 걸렸다가 block이 끝나면 lock이 풀린다.
▹ lock이 걸리는 순간부터 해당 객체에는 다른 Thread가 이 객체에 접근할 수 없게 된다.
◦ synchronized Method
▹ 한 Thread가 synchronized Method를 호출해 수행하고 있으면 해당 Method가 종료될 때까지 다른 Thread가 이 Method를 호추랗여 수행할 수 없게된다.
◦ 사용 방법
▹ 특정한 객체에 lock을 걸고자할 경우
1 2 3 | synchronized ([Reference Variable of Object]) { ...... } | cs |
▹ Method에 lock을 걸고자할 경우
1 2 3 | public synchronized void calcSum() { ...... } | cs |
· wait() & notify()
◦ Thread 동기화 효율을 높이기위해 같이 사용할 수 있는 Method
◦ 특징
▹ synchronized Keyword를 사용할 떄 한 Thread가 객체에 lock을 걸고 어떤 조건이 만족될 때까지 기다려야하는 경우 해당 Thread를 그대로 두면 해당 객체를 사용하려는 다른 Thread들은 lock이 풀릴 때까지 기다려햐하는 비효율적인 상황이 발생하며 이 상황을 개선하기 위해 wait()와 notify() Method를 사용한다.
▹ 모든 객체에서 호출 가능하다.
▹ Object에 정의되어 있다.
▹ synchronized Block 내에서만 사용 가능하다.
▹ 보다 효율적인 동기화를 가능하게 한다.
◦ wait()
▹ Thread가 호출시 해당 Thread가 걸었던 모든 lock을 풀고 waiting pool에서 기다리게 된다.
◦ notify()
▹ 객체의 waiting pool에서 벗어나 실행대기 열에서 자신이 실행될 차례를 기다리는 상태가 된다.
▹ notify()는 하나의 Thread만을 깨우고 notifyAll()는 모든 Thread를 깨운다.
▹ notifyAll()을 사용해 모든 Thread를 실행대기 열에서 대기시키는 것이 안전하다.
'Java > Theory' 카테고리의 다른 글
Chapter 10. AWT & Applet (0) | 2015.09.05 |
---|---|
Chapter 8. Useful Classes II (0) | 2015.08.19 |
Chapter 7. Useful Classes I (0) | 2015.08.19 |
Chapter 6. Collection Framework (0) | 2015.08.19 |
Chapter 5. Inner Class (0) | 2015.08.19 |