면접 준비 용 자바 개념 정리 6
2018-09-25
1. 멀티 스레드
- 멀티스레드는동시성(Concurrency) 또는 병렬성(Parallelism)으로 실행된다.
- 동시성은 멀티 작업을 위해 하나의 코어에서 멀티 스레드가 번갈아가며 실행하는 성질을 의미한다
- 병렬성은 멀티 작업을 위해 멀티 코어에서 개별 스레드를 동시에 실행하는 성질을 말한다.
- 스레드 개수가 코어의 수보다 많은 경우 스레드를 어떤 순서에 의해 동시성으로 실행할 것인가를 결정해야 하는데, 이것을 스레드 스케줄링이라고 한다.
- 스레드 스케줄링에 의해 스레드들은 아주 짧은 시간에 번갈아가면서 run()메소드를 조금씩 수행한다.
- 자바의 스레드 스케줄링은 우선순위 방식과 정해진 time slice 만큼 실행하고 다른 스레드를 실행하는 방식의 순환 할당(round-robin) 방식이 있다.
- 우선 순위 방식은 개발자가 코드로 제어할 수 있다.
2. 동기화 메소드와 동기화 블록
- 스레드가 사용 중인 객체를 다른 스레드가 변경할 수 없도록 하려면 스레드 작업이 끝날 때까지 객체에 잠금을 걸어서 다른 스레드가 사용할 수 없도록 해야 한다.
- 메소드 전체를 임계 영역으로 만들고 싶다면 메소드 선언에 synchronized 키워드를 붙이고, 일부 내용만 임계 영역으로 만들고 싶다면 동기화 블럭을 만든다.
// 1) 동기화 메소드
public synchronized void method(){
// 단 하나의 쓰레드만 실행(임계영역)
}
// 2) 동기화 블럭
public void method(){
// 여러 쓰레드가 실행 가능한 영역
...
synchronized(공유객체){
// 단 하나의 쓰레드만 실행
}
// 여러 쓰레드가 실행 가능한 영역
...
}
3. 스레드 상태
- 객체 생성(NEW) : 스레드 객체 생성. 아직 start() 메소드가 호출되지 않은 상태
- 실행 대기(RUNNABLE): 아직 스케줄링이 되지 않아 실행을 기다리는 상태
- 실행(RUNNING): 실행 대기 상태에 있는 스레드 중에서 스레드 스케줄링으로 선택된 스레드가 CPU를 점유하고 run() 메소들르 실행함. 실행 상태의 스레드는 run() 메소드를 모두 실행하기 전에는 스레드 스케줄링에 의해 다시 실행 대기 상태로 돌아갈 수 있다
- 종료(TERMINATED): 스레드가 실행 대기 상태와 실행 상태를 번갈아 가면서 자신의 run() 메소드를 조금씩 실행하는데, 실행 상태에서 run() 메소드가 종료되면 더 이상 실행 할 코드가 없으므로 스레드의 실행은 멈추게 된다.
- 일시정지: WAITING(다른 스레드가 통지할 때까지 기다리는 상태), TIMED_WAITING(주어진 시간동안 기다리는 상태), BLOCKED(사용하고자 하는 객체의 락이 풀릴 때까지 기다리는 상태)
4. 스레드 상태 제어
- interrupt(): 일시 정지 상태의 스레드에서 InterruptedException 예외를 발생시켜, 예외 처리 코드(catch)에서 실행 대기 상태로 가거나 종료 상태로 갈 수 있도록 한다.
- Thread.sleep(long millis): 실행 중인 스레드가 일정 시간 정지 상태가 되고, 다시 실행 대기 상태로 돌아간다.
try {
Thread.sleep(1000);
} catch(InterruptedException e){
// 주어진 시간이 되기 전에 interrupt() 메소드가 호출되면 InterruptedException이 발생
}
- Thread.yield(): 호출한 스레드는 실행 대기 상태로 돌아가고, 동일한 우선순위 또는 높은 우선순위를 갖는 다른 스레드가 실행 기회를 가질 수 있도록 해준다.
- join(): 다른 스레드가 종료될 때까지 기다렸다가 실행하는 경우 사용.
- wait(), notify(), notifyAll(): 두 개의 스레드를 교대로 번갈아가며 실행해야 하는 경우에 사용. 자신의 작업이 끝나면 상대방 스레드를 일시 정지 상태에서 풀어주고, 자신은 일시 정지 상태로 만드는 것. 공유 객체는 두 스레드가 작업할 내용을 각각 동기화 메소드로 구분해 놓는다. 한 스레드가 작업을 완료하면 notify() 메소드를 호출해서 일시 정지 상태에 있는 다른 스레드를 실행 대기 상태로 만들어 놓고, 자신은 wait() 메소드를 호출하여 일시 정지 상태로 만든다. wait() 대신 wait(long timeout)을 사용하면 notify() 호출없이 지정된 시간이 지난 후 스레드가 자동적으로 실행 대기 상태가 된다. 이 메소드 들은 동기화 메소드 또는 동기화 블럭 내에서만 사용 가능하다.
- interrupt(), stop 플래그: 스레드는 자신의 run() 메소드가 모두 실행되면 자동적으로 종료되지만, 경우에 따라서는 실행 중인 스레드를 즉시 종료해야할 필요가 있다. interrupt() 메소드는 스레드가 일시 정지 상태에 있을 때 InterruptedException 예외를 발생시키는 역할을 한다.
// stop 플래그 이용 방법
public class ThreadXXX extends Thread {
private boolean stop; // stop 플래그
public void run() {
while(!stop){
}
}
}
// interrupt() 이용 방법
public class Example {
public static void main(String[] args){
Thread thread = new ExampleThread();
thread.start();
try{
Thread.sleep(1000);
} catch(InterruptedException e) {
}
thread.interrupt();
}
public class ExampleThread extends Thread {
public void run() {
try {
while(true){
System.out.println("running..");
Thread.sleep(1); // interruptedException 발생0
}
} catch (InterruptedException e){
}
}
}
5. 스레드풀
- 병렬 작업 처리가 많아지면 스레드 개수가 증가되고 그에 따른 스레드 생성과 스케줄링으로 인해 CPU가 바빠져 메모리 사용량이 능어난다. 이는 애플리케이션 성능의 저하를 유발한다.
- 갑작스런 병렬 작업의 폭증으로 인한 스레드의 폭증을 막으려면 스레드풀(Thread Pool)을 사용해야 한다.
- 스레드풀을 작업 처리에 사용되는 스레드를 제한된 개수만큼 정해놓고 작업 큐에 들어오는 작업들을 하나씩 스레드가 맡아 처리한다. 작업 처리가 끝난 스레드는 다시 작업 큐에서 새로운 작업을 가져와 처리한다.
- 그렇기 때문에 작업 처리 요청이 폭증되어도 스레드의 전체 개수가 늘어나지 않으므로 애플리케이션의 성능이 급격히 저하되지 않는다.