Chap 12. 스레드
1. 멀티 스레드
- 운영체제에서는 실행중인 하나의 애플리케이션을 프로세스라고 부른다.
- 하나의 애플리케이션은 멀티 프로세스를 만들기도 한다.
1) 스레드
-
운영체제는 두 가지 이상의 작업을 동시에 처리하는 멀티 태스킹을 할 수 있도록 CPU 및 메모리 자원을 프로세스마다 적절히 할당해주고, 병렬로 실행시킨다.
-
멀티 태스킹은 꼭 멀티 프로세스를 뜻하는 것은 아니다. 한 프로세스 내에서 멀티 태스킹을 할 수 있도록 만들어 진 애플리케이션이 존재한다.(멀티 스레드)
- 동영상 재생과 음악 재생 작업을 동시에 처리하는 미디어 플레이어
-
스레드는 하나의 코드 실행 흐름이다.
-
한 프로세스 내에 스레드가 2개라면 2개의 코드 실행 흐름이 생긴다는 의미이다. 이를 멀티 스레드라고 한다.
-
멀티 프로세스는 각 프로세스마다 서로 독립적이기 때문에 하나의 프로세스에서 오류가 발생해도 다른 프로세스에 영향을 미치지 않는다.
-
멀티 스레드는 하나의 프로세스 내부에 생성되기 때문에 하나의 스레드가 예외를 발생시키면 다른 스레드에 영향을 미친다.
-
때문에 멀티 스레드에서는 예외처리에 만전을 기해야한다.
2) 메인 스레드
-
모든 애플리케이션은 메인 스레드가 main() 메소드를 실행하면서 시작한다.
-
main() 메소드의 마지막 코드를 실행하거나 return문을 만나면 실행이 종료된다.
-
메인 스레드는 필요에 따라 작업 스레드들을 만들어서 병렬로 코드를 실행할 수 있다. 즉, 멀티 스레드를 생성해서 멀티 태스킹을 수행할 수 있다.
-
싱글 스레드 어플리케이션은 메인 스레드가 종료하면 프로세스도 종료된다.
-
멀티 스레드 어플리케이션은 메인 스레드가 작업 스레드보다 먼저 종료되더라도 작업 스레드가 실행중이라면 프로세스는 종료되지 않는다.
3) 작업 스레드 생성과 실행
- 메인 작업 이외에 추가적인 병렬 작업의 수만큼 스레드를 생성한다.
- 자바에서는 작업 스레드도 객체로 생성되기 때문에 클래스가 필요하다.
(1) Thread 클래스로부터 직접 생성하는 방법
- 다음과 같이 Runnable을 매개값으로 갖는 생성자를 호출해야한다.
Thread thread = new Thread(Runnable target);
- Runnable을 인터페이스 타입이기 때문에 구현 객체를 만들어 대입해야한다.
- Runnable 구현 객체를 생성한 후, 이것을 매개값으로 해서 Thread 생성자를 호출해야 작업 스레드가 생성된다.
Runnable task = new Task();
Thread thread = new Thread(task);
- 익명 객체로 코드를 짤 수 있다.
- 작업 스레드는 생성 즉시 실행되는 것이 아니라, start() 메소드를 호출해야 실행된다.
(2) Thread 하위 클래스로부터 생성
- Thread 클래스를 상속한 후 run() 메소드를 재정의해서 스레드가 실행할 코드를 작성한다.
public class WorkerThread extends Thread {
@Override
public void run() {
}
}
Thread thread = new WorkerThread();
스레드 이름
- 스레드를 생성하면 자동적으로 Thread-n이라는 이름이 설정된다.
- setName() 메소드로 변경할 수 있다.
- getName() 메소드로 스레드 이름을 알 수 있다.
- 위의 두 메소드는 인스턴스 메소드이므로 스레드 객체의 참조가 필요하다. 스레드 객체의 참조를 가지고 있지 않다면, Thread 클래스의 정적 메소드인 currentThread()를 이용해서 현재 스레드의 참조를 얻을 수 있다.
Thread thread = Thread.currentThread();
4) 동기화 메소드
- 멀티 스레드 프로그램에서 스레드들이 객체를 공유해서 작업하는 경우, 스레드 A가 사용하던 개체를 스레드 B가 상태를 변경할 수 있다.
- 이런 경우가 생기지 않게 메소드 선언에 synchronized 키워드를 붙이면, 한 스레드가 동기화 메소드가 선언된 객체를 사용하는 동안 다른 스레드가 해당 객체를 사용할 수 없다. 이때 또다른 스레드의 일반 메소드는 실행 가능하다.
2. 스레드 제어
1) 스레드 상태
- 스레드 객체를 생성하고 start() 메소드를 호출하면 바로 실행되는 것이 아니라 실행 대기상태가 된다.
- 실행 대기 상태란 언제든지 실행할 준비가 되어있는 상태를 말한다.
- 운영체제는 실행 대기 상태에 있는 스레드 중에서 하나를 선택해서 실행 상태로 만든다.
- 실행 상태의 스레드는 run() 메소드를 모두 실행하기 전에 다시 실행 대기상태로 돌아갈 수 있으며, 실행 대기 상태에 있는 다른 스레드가 선택되어 실행 상태가 되기도 한다.
- 실행 상태에서 run() 메소드의 내용이 모두 실행되면 스레드의 실행이 멈추고 종료상태가 된다.
2) 스레드 상태 제어
- 실행중인 스레드의 상태를 변경하는 것을 말한다.
주어진 시간동안 일시 정지
- Thread 클래스의 정적 메소드인 sleep()을 사용하면 실행 중인 스레드를 일정 시간 멈추게할 수 있다.
스레드의 안전한 종료
- stop()메소드가 제공되지만, 이 메소드를 사용하면 사용 중이던 자원이 불안정한 상태로 남겨지기 때문에 사용되지 않는다.
- stop() 플래그를 이용하는 방법
public class threadA extends Thread {
private boolean stop;
public void run() {
while( !stop ) {
}
}
}
- interrupt() 메소드를 실행하는 방법
- interrupt() 메소드는 스레드가 일시 정지 상태에 있을 때 interruptedException을 발생시키는 역할을 한다. 이를 이용하면 run() 메소드를 정상 종료할 수 있다.
- interrup()를 호출한 후 정적메소드 interrupted()나 인스턴스 메소드 inInterrupted()를 활용하여 종료할 수도 있다.
3) 데몬 스레드
- 주 스레드의 작업을 돕는 보조적인 역할을 수행하는 스레드이다.
- 주 스데드가 종료되면 데몬 스레드는 강제적으로 자동 종료된다.
- start() 메소드 호출 전에 setDaemon(true)를 호출하면 된다.
- 현재 실행중인 메소드의 데몬스레드 여부는 isDaemon() 메소드를 사용하면 된다.