: 실행중인 프로그램
New
: 프로세스가 메모리에 올라와 실행 준비를 완료한 상태 (스레드가 생성되고 start()가 호출되지 않은 상태)Ready
: 프로세서에서 실행되기 위해 대기하는 상태 (start()가 호출된 상태)Running
: Ready 상태에 있는 프로세스 중 하나가 CPU 스케줄러를 통해 선택이 되어 작업을 수행하는 상태Blocked
: 특정 자원이나 이벤트를 기다리는 상태Terminate
: 프로세스가 종료된 상태: 한 프로세스 내에서 나뉘어진 하나 이상의 실행 단위
* main스레드의 경우 Single Thread여서 순차적으로 실행됨
New
: 스레드가 실행 준비를 완료한 상태Runnable
: start()가 호출되어 실행될 수 있는 상태Waiting
: 다른 스레드가 통지할 때까지 기다리는 상태Timed_Waiting
:정해진 시간동안 기다리는 상태Blocked
: 사용하고자 하는 객체의 잠금(Lock)이 풀릴 때까지 대기하는 상태Terminate
: 실행이 종료된 상태메소드 명 | 설명 | 상태 변화 |
---|---|---|
sleep() | 실행 중인 스레드를 파라미터에 주어진 시간만큼 잠시 Timed_waiting 하게 만듦. | Running → (메소드 호출 후) Timed_waiting → Runnable |
join() | join() 메소드를 호출한 스레드가 종료될 때까지 기다리도록 스레드를 Wait 상태로 만듦. | Running → (메소드 호출 후) Waiting → Runnable |
interrupt() | 일시정지 상태인 스레드를 깨워서 실행가능한 상태(실행대기 상태)로 만듦. (단지 작업 취소를 요청만 하는 것이지 스레드를 강제로 종료시키지는 못함) | Waiting → (메소드 호출 후) Runnable |
yield() | 자신에게 주어진 실행시간을 동일하거나 높은 우선순위를 가진 스레드에게 양보함. | Running → (메소드 호출 후) Runnable |
* Object 클래스에 정의되어 있는 메소드
: synchronized 블럭 내에서만 사용 가능 (호출하는 스레드가 반드시 고유락을 갖고 있어야 함)
wait()
: 실행 중이던 스레드는 해당 객체의 waiting pool에서 notify( )를 기다림.notify()
: 해당 객체의 waiting pool에 있던 스레드 중 임의의 스레드만 Runnable하게 만듦.notifyAll()
: waiting pool에 있는 모든 스레드를 Runnable하게 만듦.New(생성)
하고 start()
를 호출하면 바로 Running하는 것이 아니라 실행대기열에 저장되어 차례가 될 때까지 기다려야한다.yield()
를 만나면 다시 Runnable상태가 되고 다음 차례의 스레드가 Running 상태가 된다.sleep()
, wait()
, join()
, I/O block
에 의해 Waiting 상태가 될 수 있다. notify()
, interrupt()
가 호출되면 일시정지상태를 벗어나 다시 실행대기열에 저장되어 차례를 기다리게 된다.stop()
이 호출되면 스레드는 소멸된다.스터디룸을 빌리는 경우로 생각해봤을 때
프로세스의 경우 항상 사용해야되는 책상과 의자를 사용이 끝나면 모두 치우고, 새로운 예약이 들어올때마다 새로 setting 해야한다.(context switching) 하지만 스레드의 경우 책상과 의자를 (공유되는 자원) 프로세스처럼 매번 다시 setting 할 필요가 없다 (캐싱 적중률 ↑)
: 멀티 프로세스
는 두 개 이상 다수의 프로세서(CPU)가 두개 이상의 작업을 동시에 처리하는 것이고,
멀티 스레드
는 하나의 프로세스에 여러 스레드로 자원을 공유하며 작업을 나누어 수행하는 것이다.
즉, 한 어플리케이션에 대한 작업을 동시에 하기 위한 2가지 처리방식이라고 할 수 있다.
멀티 프로세스
는 독립적인 메모리를 가지고 있지만, 멀티 스레드
는 자원을 공유하여 스레드끼리 긴밀하게 연결되어 있기 때문에 하나의 스레드에 문제가 생기면 전체 프로세스에 영향을 준다.멀티 프로세스
는 개별 메모리를 사용하기 때문에 context switching 비용이 큰 반면,멀티 스레드
는 공유된 자원을 사용하여 메모리가 효율적이고 context switching 비용 역시 적다.멀티 프로세스
는 동기화 작업이 필요하지 않지만, 멀티 스레드
는 자원을 공유하기 때문에 동기화나 교착상태 등의 문제가 발생할 수 있어 공유자원 관리를 해줘야할 필요 있다.CPU의 코어는 한 번에 단 하나의 작업만 수행할 수 있는데,
동시에 실행이 되는 것처럼 보이기 위해서 실행 단위는 시분할로 cpu를 점유하여 context switching을 함
* Context Switching이란?
: CPU에서 여러 프로세스를 돌아가면서 작업을 처리하는 과정
public class 클래스이름 extends Thread
Thread 스레드이름 = new Thread();
public class 클래스이름 implements Runnable
Thread 스레드이름 = new Thread(new 클래스이름());
: 한 스레드가 진행 중인 작업을 다른 스레드가 간섭하지 못하도록 막는 것
멀티스레드 프로세스의 경우 여러 스레드가 같은 프로세스 내의 자원을 공유하여 작업하기 때문에 서로의 작업에 영향을 주게 된다.
그럼 이러한 문제는 어떻게 해결할 수 있을까?
공유 데이터를 사용하는 코드 영역을 임계 영역 (critical section)
으로 지정해놓고, 공유 데이터(객체)가 가지고 있는 lock
을 획득한 단 하나의 스레드만 이 영역 내의 코드를 수행할 수 있게 한다. 이렇게 되면 해당 스레드가 임계 영역
내의 모든 코드를 수행하고 벗어나서 lock을 반납해야만 다른 스레드가 반납된 lock
을 획득하여 임계 영역의 코드를 수행할 수 있게 된다.
public syncronized void 메소드명() {
//
}
lock
을 얻어 작업을 수행하다가 메소드가 종료되면 lock
을 반납syncronized (객체의 참조변수) { /*
// 여기가 synchronized 블럭
} */
lock
을 걸고자하는 객체를 참조하는 것이어야 함synchronized 블럭
으로 들어가면서부터 스레드는 지정된 객체의 lock
을 얻게 되고, 블럭을 벗어나면 lock
을 반납한 스레드가 lock
을 보유한 채로 메소드를 실행 가능할 때까지 기다리게 된다면, 다른 스레드들은 해당 객체의 lock
을 기다리느라 다른 작업들까지 원활하게 진행되지 못할 것이다.
그럼 이러한 문제는 어떻게 해결할 수 있을까?
동기화된 임계영역
의 코드를 수행하다가 작업을 더 이상 진행할 상황이 아니면, 일단 wait()
을 호출하여 스레드가 lock
을 반납하고 기다리게 한다. 그러면 다른 스레드가 lock
을 얻어 해당 객체에 대한 작업을 수행할 수 있게 된다. 나중에 작업을 진행할 수 있는 상황이 되면 notify()
를 호출해서 작업을 중단했던 스레드가 다시 lock
을 얻어 작업을 진행할 수 있게 한다.
run()
이 아닌 start()
를 호출할까?run()
은 단순히 클래스에 선언된 메소드를 호출하는 것이지만,
start()
는 call stack을 생성한 다음 run()
을 호출해 생성된 call stack에 run()
을 올려 스레드가 독립된 공간에서 작업을 수행한다.
main메소드에서 스레드 두개를 생성한 후 try-catch문에서 thread1에 대해 sleep()
을 호출했는데 멈추지 않고 가장 먼저 종료가 되었다. 이유가 무엇일까?
public static void main(String args[]) {
Thread thread1 = new Thread();
Thread thread2 = new Thread();
thread1.start();
thread2.start();
try {
thread1.sleep(2000);
} catch(InterruptedException e) {}
}
sleep( )은 항상 현재 실행 중인 스레드에 대해 작동하기 때문에 'thread1.sleep(2000)'과 같이 호출하였어도 실제로는 main메소드가 실행하는 main스레드가 영향을 받기 때문이다. 그래서 sleep()
은 'Thread.sleep( )'과 같이 호출해야 한다.