열여섯번째날 [멀티 스레드(2) - 동기화 메소드 / 동기화 블록 / 스레드 상태 / 데몬 스레드 / 스레드 그룹 / 스레드 풀]
목차
1. 동기화 메소드와 동기화 블록
2. 스레드 상태
3. 데몬 스레드
4. 스레드 그룹
5. 스레드 풀
* 스레드 목적 : 여러개의 작업을 동시에, 병렬적으로.
동기화 메소드와 동기화 블록
공유 객체를 사용할 때 주의할 점
* 멀티 스레드가 하나의 객체를 공유해서 생기는 오류
동기화 메소드 및 동기화 블록 : synchronized
* 스레드가 사용중인 객체를 다른 스레드가 변경할 수 없도록 작업이 끝날때까지 객체에 잠금을 건다.
* 단 하나의 스레드만 실행할 수 있는 메소드 또는 블록
* 다른 스레드는 메소드나 블록 실행이 끝날 때까지 대기해야 함
* 임계 영역(critical section) : 단 하나의 스레드만 실행할 수 있는 코드 영역
* 임계 영역을 지정하기 위해 동기화 메소드 & 동기화 블록을 제공
* 동기화 메소드
public synchronized void method(){
임계 영역; //단 하나의 스레드만 실행
}
* 동기화 블록
public void method(){
//여러 스레드가 실행 가능 영역
...
synchronized(공유하는 객체){
임계영역 //단 하나의 스레드만 실행
}
//여러 스레드가 실행 가능 영역
...
}
스레드 상태
* 일반적인 상태
* 스레드에 일시정지 상태를 도입한 경우
* 스레드 상태에 대한 설명과 열거 상수들
스레드 상태 제어
* 실행 중인 스레드의 상태를 변경하는 것
* 상태 변화를 가져오는 메소드의 종류 : 취소선 가진 메소드는 사용X
주어진 시간동안 일시 정지 : sleep()
try{
Thread.sleep(1000);
}catch(InterruptedException e){
//interrupt() 메소드가 호출되면 실행
}
* 얼마 동안 일시 정지 상태로 있을 것인지 밀리세컨드(1/1000) 단위로 지정
* 일시 정지 상태에서 interrupt() 메소드 호출 : InterruptedException 발생함
다른 스레드에게 실행 양보 : yield()
* 무의미한 반복을 하는 스레드일 경우 : 다른 스레드에게 실행 양보, 자신은 실행 대기 상태로 가는 것
public void run(){
while(true){
if(work){
System.out.println("ThreadA 작업 내용");
}else{
Thread.yield();
}
}
}
다른 스레드의 종료를 기다림 : join()
* 계산 작업을 하는 스레드가 모든 계산 작업 마쳤을 때, 결과값을 받아 이용하는 경우 주로 사용
스레드 간 협업: wait(), notify(), notifyAll()
* 두 개의 스레드가 교대로 번갈아 가며 실행해야 할 경우 주로 사용함
* 동기화 메소드 또는 블록에서만 호출 가능한 Object의 메소드
* 두 개의 스레드가 교대로 번갈아가며 실행해야 할 경우 주로 사용
스레드의 안전한 종료 : stop플래그, interrupt()
* 경우에 따라 실행 중인 스레드를 즉시 종료해야 할 필요가 있을 때 사용
* stop() 메소드 사용시
- 스레드가 즉시 종료되는 편리함이 있음
- Deprecated : 사용 중이던 자원들이 불안전한 상태로 남겨짐
* stop 플래그 이용하는 방법
- 플래그 : true, false를 이용함
- stop플래그로 메소드의 정상 종료 유도
private boolean stop; //stop 플래그 필드
public void run(){
while(!stop){ //stop이 true가 되면 run()이 종료됨
스레드가 반복 실행하는 코드;
}
//스레드가 사용한 자원 정리
}
* interrupt() 메소드 이용하는 방법
- 스레드가 일시 정지일 상태일 경우 InterruptedException 발생시킴
- 실행 대기 또는 실행상태에서는 InterruptedException 발생하지 않음
- 일시 정지 상태로 만들지 않고 while문 빠져나오는 방법으로도 사용됨
데몬 스레드
* 주 스레드의 작업을 돋는 보조적인 역할을 수행하는 스레드
* 주 스레드가 종료되면, 데몬 스레드도 강제적으로 자동 종료
* 스레드를 데몬 스레드로 만들기
- 주 스레드가 데몬이 될 스레드의 setDaemon(true)호출
- 반드시 start() 메소드 호출 전에 setDaemon(true) 호출 : 그렇지 않으면 IllegalThreadStateException 발생
public static void main(Stirng[] args{
AutoSaveThread thread = new AutoSaveThread();
thread.setDaemon(true);
thread.start();
...
}
* 현재 실행중인 스레드가 데몬 스레드인지 구별법
- isDaemon() 메소드 리턴값 조사 : 데몬 스레드일 경우 true 리턴
스레드 그룹
* 관련된 스레드를 묶어 관리 목적으로 이용
* 스레드 그룹은 계층적으로 하위 스레드 그룹 가질 수 있음
* 자동 생성되는 스레드 그룹
- system 그룹 : JVM 운영에 필요한 스레드들 포함
- system / main 그룹 : 메인 스레드 포함
* 스레드는 반드시 하나의 스레드 그룹에 포함
- 기본적으로 자신을 생성한 스레드와 같은 스레드 그룹
- 스레드 그룹에 포함시키지 않으면 기본적으로 system/main 그룹
스레드 그룹 이름 얻기
* 현재 스레드가 속한 스레드 그룹 이름 얻기
ThreadGroup group = Thread.currentThread.getthreadGroup();
String groupName = group.getName();
* 프로세스 내에서 실행하는 모든 스레드에 대한 정보 얻기
- getAllStackTrace()는 Map 타입의 객체를 리턴
- 키는 스레드 / 값은 스레드 상태 기록을 가지고 있는 배열(StackTraceElement[])
Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
스레드 그룹 생성
* 부모 그룹을 지정하지 않으면? 현재 속한 그룹의 하위 그룹으로 생성됨
ThreadGroup tg = new ThreadGroup(String name);
ThreadGroup tg = new ThreadGroup(ThreadGroup parent, String name);
스레드 그룹의 일괄 interrupt()
* 스레드 그룹의 interrupt() 호출 시 소속된 모든 스레드의 interrupt() 호출
ex) 10개의 스레드들을 모두 종료시키기 위해, 각 스레드에서 메소드를 10번 호출해도 되지만! 스레드들이 같은 그룹에 소속되어있으면 , 스레드 그룹의 interrupt() 메소드를 한번만 호출해주면 됨
스레드풀
* 스레드 폭증으로 일어나는 현상
- 병렬 작업 처리가 많아지면 스레드 개수 증가
- 스레드 생성과 스케줄링으로 인해 CPU가 바빠짐
- 메모리 사용량이 늘어남
- 애플리케이션의 성능 급격히 저하
* 스레드 풀(Thread Pool)
- 작업 처리에 사용되는 스레드를 제한된 개수만큼 미리 생성
- 작업 큐(Queue)에 들어오는 작업들을 하나씩 스레드가 맡아 처리
- 작업 처리가 끝난 스레드는 작업 결과를 애플리케이션으로 전달
- 스레드는 다시 작업 큐에서 새로운 작업을 가져와 처리