Java08_멀티스레드
- 이 글은
이것이 자바다 - 신용권 지음
책을 공부하며 작성한 글입니다.
1. 멀티스레드 기본 용어
1-1. 프로세스와 스레드
- 프로세스 : 실행 중인 하나의 애플리케이션
멀티태스킹 : 두 가지 이상의 작업을 동시에 처리
- 멀티프로세스
- 애플리케이션 단위의 멀티 태스킹
- 하나의 프로세스에서 오류가 발생해도 다른 프로세스에 영향 X
- 멀티스레드
- 하나의 프로세스가 두 가지 이상의 작업을 처리
- 애플리케이션 내에서의 멀티태스킹
- 하나의 스레드에서 예외 발생시 다른 프로세스 자체가 종료되므로 예외처리 필수
1-2. 메인스레드
- 싱글 스레드 애플리케이션에서는 메인스레드가 종료하면 프로세스도 종료된다.
- 멀티 스레드 애플리케이션에서는 실행 중인 스레드가 하나라도 있다면, 프로세스는 종료되지 않는다.
- 메인 스레드가 작업 스레드보다 먼저 종료되더라도 작업 스레드가 계속 실행중이라면 프로세스는 종료되지 않음.
2. 작업 스레드 생성과 실행
java.lang.Thread
클래스를 직접 객체화하여 생성- Thread를 상속하여 하위 클래스를 만들어 생성
2-1. Thread클래스로부터 직접 생성
- Runnable을 매개값으로 갖는 생성자를 호출해야한다.
- run( ) 메소드를 재정의한다.
- start( )메소드를 호출하면 Runnable의 run( )메소드를 실행하면서 자신의 작업 처리
Thread thread = new Thread(Runnable target);
public class BeepTask implements Runnable{ }
- 실행순서
- 메인스레드
- (main) 스레드객체 생성 (class Task implements Runnable)
- (main) start( ) 메소드 호출
- 스레드 객체에서 run() 메소드 실행
- 작업 스레드 실행
2-2. Thread 하위 클래스로부터 생성
- Thread클래스를 상속하여 run 메소드를 재정의(overriding)해서 스레드가 실행할 코드를 작성
public class WorkerThread extends Thread {
@Override
public void run(){
//스레드가 실행할 코드
}
}
- 실행순서
- 메인스레드
- (main) 스레드객체 생성
- (main) start( )메소드 호출
- 스레드 객체의 run() 메소드 실행
2-3. 스레드 이름
- 스레드 객체의 참조
Thread thread = Thread.currentThread();
- 스레드 이름 설정
thread.setName("스레드 이름);
- 스레드 이름 가져오기
thread.getName();
3. 스레드 우선순위
- 동시성
- 멀티 작업을 위해 멀티 코어에서 개별 스레드를 동시에 실행하는 성질
- 싱글코어 CPU 멀티스레드 작업은 병렬적으로 실행되는 것 처럼 보이지만, 번갈아가며 실행하는 동시성 작업
- 스레드 스케쥴링
- 스레드의 개수가 코어의 수보다 많을 경우, 스레드를 어떤 순서에 의해 동시성으로 실행할 것인가를 결정
- 아주 짧은 시간에 번갈아가며 run() 메소드를 조금씩 실행
- 우선순위방식 / 순환할당방식
- 우선순위방식 : 우선순위가 높은 스레드가 실행 상태를 더 많이 가지도록 스케쥴링 ( 개발자가 제어 가능 )
- 순환할당방식 : 시간 할당량을 정해서 하나의 스레드를 정해진 시간만큼 실행하고 다시 다른 스레드를 실행. ( 개발자 제어 불가 )
4. 동기화 메소드와 동기화 블록
- 멈티 스레드 프로그램에서는 스레드들이 객체를 공유해서 작업해야하는 경우가 있다.
- 스레드 A를 사용하던 객체가 스레드 B에 의해 상태가 변경될 수 있으므로 다른 결과를 산출할 수도 있다.
4-1. 동기화
- 임계영역 : 단 하나의 스레드만을 실행할 수 있는 코드 영역
- 자바는 임계영역을 지정하기 위해
synbchronized
메소드와 동기화 블록을 제공한다. - 스레드는 동기화 메소드를 실행하는 즉시 객체에 잠금이 일어나고, 실행 종료시 잠금이 풀림
5. 스레드 상태
- 실행대기 상태
- 스케쥴링이 되지 않아서 실행을 기다리고 있는 상태
- 종류
- RUNNABLE
- 실행상태
- 실행대기 상태의 스레드 중 스레드 스케쥴링으로 선택된 스레드가 CPU를 점유하고 run( )메소드를 실행하는 상태
- 실행상태의 스레드는 run( )메소드를 모두 실행하기 전에 스레드 스케줄링에 의해 다시 실행 대기상태로 돌아갈 수 있음
- 실행 대기상태에 있는 다른 스레드가 선택되어 실행상태가 된다.
- 실행과 실행대기 상태를 번갈아가며 run( )메소드를 조금씩 실행
- 종료상태
- run( )메소드 종료시 더 이상 실행할 코드가 없으므로, 실행이 멈추게 된다.
- 종류
- TERMINATED
- 일시정지상태
- 실행상태에서 대기가 아닌 일시정지 상태가 되기도 함.
- 종류
- WAITING, TIMED_WAITING, BLOCKED
6. 스레드 상태 제어
메소드 | 설명 |
---|---|
interrupt() |
Thread 클래스 일시정지 상태의 스레드에서 InterruptedException 예외를 발생시켜 예외처리코드(catch)에서 실행대기 상태로 가거나 종료 상태로 갈 수 있도록 한다. |
notify() notifyAll() |
Object 클래스 동기화 블록 내에서 wait()메소드에 의해 일시정지 상태에 있는 스레드를 실행 대기 상태로 만든다. |
resume() |
Thread 클래스 suspend()메소드에 의해 일시정지 상태의 스레드를 실행대기 상태로 만든다. - Deprecated(대신 notify(), notifyAll() 사용) |
sleep(long mills) sleep(long mills, int nanos) |
Thread 클래스 주어진 시간동안 스레드를 일시정지 상태로 만든다. 주어진 시간이 지나면 자동적으로 실행대기 상태가 된다. |
join() join(long mills) join(long mills, int nanos) |
Thread 클래스 join()메소드를 호출한 스레드는 일시정지 상태가 된다. 실행대기 상태로 가려면, join()메소드를 멤버로 가지는 스레드가 종료되거나 매개값으로 주어진 시간이 지나야 한다. |
wait() wait(long mills) wait(long mills, int nanos) |
Object 클래스 동기화(synchronized)블록 내에서 스레드를 일시정지 상태로 만든다. 매개값으로 주어진 시간이 지나면 자동적으로 실행대기 상태가 된다. 시간이 주어지지 않으면 notifiy(), notifyAll()메소드에 의해 실행대기 상태로 갈 수 있다. |
suspend() |
Thread 클래스 스레드를 일시정지 상태로 만든다. resume() 메소드를 호출하면 다시 실행 대기 상태가 된다. - Deprecated (대신 wait()사용) |
yield() |
Thread 클래스 실행중에 우선순위가 동일한 다른 스레드에게 실행을 양보하고 실행 대기 상태가 된다. |
stop() |
Thread 클래스 스레드를 즉시 종료 시킨다. - Deprecated |
7. 데몬스레드
- 주 스레드의 작업을 돕는 보조적인 역할을 하는 스레드
- 주 스레드가 종료되면 데몬 스레드는 강제적으로 자동 종료 ( 주 스레드가 종료되면 존재 의미가 없음 )
- 워드프로세스, 미디어플레이어, JVM이 종료되면 같이 종료.
- 스레드를 데몬으로 만들기 위해서는 주 스레드가 데몬이 될 스레드의 setDaemon(true)를 호출해주면 된다.
public static void main(String[] args){
AutoSavThread thread = new AutoSaveThread();
thread.setDaemon(true);
thread.start();
}
8. 스레드 그룹
- 관련된 스레드를 묶어서 관리
- JVM이 실행되면 system스레드 그룹을 만들고 JVM운영에 필요한 스레드를 생성하여 system스레드 그룹에 포함시킨다.
- system의 하위 스레드 그룹으로 main을 만들고 메인 스레드를 main스레드 그룹에 포함시킨다.
- 스레드는 반드시 하나의 스레드 그룹에 포함된다.
- 명시적으로 스레드 그룹에 포함시키지 않으면 기본적으로 자신을 생성한 스레드와 같은 스레드 그룹에 속한다.
- 작업스레드 대부분은 main스레드가 생성하므로 기본적으로 main스레드 그룹에 속하게 됨.
8-1. 스레드 그룹 이름 얻기
ThreadGroup group = Thread.currentThread().getThreadGroup();
// 스레드그룹 이름얻기
String groupName = group.getName();
// 모든 정보 얻기
Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
8-2. 스레드 그룹 생성하기
ThreadGroup tg = new ThreadGroup(String name);
ThreadGroup tg = new ThreadGroup(ThreadGroup parent, String name);
9. 스레드풀
- 병렬 작업 처리가 많아지면 스레드 개수가 증가되고, 스레드 생성과 스케쥴링으로 인해 CPU가 바빠져 메모리 사용량이 늘어난다.
- 이러한 병렬 작업의 폭증으로 인한 스레드의 폭증을 막으려면 ThreadPool을 사용해야 한다.
- 작업 처리에 사용되는 스레드를 제한된 개수만큼 정해놓고 작업 큐(Queue)에 들어오는 작업들을 하나씩 스레드가 맡아 처리
- java.util.concurrent 패키지에서
ExecutorService
인터페이스와Executors
클래스를 제공