스레드(Thread)
- 프로그램 코드를 이동하면서 실행하는 하나의 제어
자바의 멀티태스킹
- 멀티쓰레딩만 가능
- 자바에 프로세스 개념은 존재하지 않고, 스레드 개념만 존재- 스레드는 실행 단위이자 스케쥴링 단위
- 하나의 응용 프로그램은 여러 개의 스레드로 구성 가능
자바 스레드
- JVM(Java Virtual Machine)에 의해 스케쥴되는 실행 단위의 코드 블럭
- 스레드의 생명 주기는 JVM에 의해 관리
- 하나의 JVM은 하나의 자바 응용 프로그램만 실행
- 하나의 응용 프로그램은 하나 이상의 스레드로 구성 가능
스레드를 만드는 2가지 방법
- java.lang.Thread 클래스를 이용하는 경우
- java.lang.Runnable 인터페이스를 이용하는 경우
1. Thread 클래스를 이용한 스레드 생성
1) Thread 클래스 상속, 새 클래스 작성
2) run() 메소드 오버라이딩
class TestThread extends Thread {
...
public void run() {
// run() 메소드 오버라이딩
...
}
}
3) 스레드 객체 생성
TestThread thread = new TestThread();
4) 스레드 시작 - start() 메소드 호출
thread.start();
- 주의 사항
- run() 메소드가 종료되면 스레드는 종료됨
- 한 번 종료된 스레드는 다시 시작할 수 없음 = 다시 스레드 객체를 생성하고 등록해야 함
- 스레드에서 다른 스레드를 강제 종료 가능
2. Runnable 인터페이스로 스레드 생성
1) Runnable 인터페이스로 새 클래스 구현
2) 스레드 코드 작성
class TestRunnable implements Runnable {
...
public void run() {
// run() 메소드 구현
...
}
}
3) 스레드 객체 생성
Thread thread = new Thread(new TestRunnable());
4) 스레드 시작
thread.start();
Thread 클래스 상속과 Runnable 인터페이스 구현의 차이
- 자바는 다중 상속을 지원하지 않는다. 그렇기 때문에 Thread 클래스를 상속받는 경우, 다른 클래스를 상속받을 수 없다. 그렇기 때문에 Runnable 인터페이스를 구현하는 것이 일반적이다.
스레드 상태 6가지
- NEW : 스레드가 생성되었지만 아직 실행할 준비가 되지 않음
- RUNNABLE : 스레드가 JVM에 의해 실행되고 있거나 실행 준비되어 스케쥴링을 기다리는 상태
- WAITING : 다른 스레드가 notify(), notifyAll()을 불러주기를 기다리고 있는 상태, 보통 스레드 동기화를 위해 사용
- TIMED_WAITING : 스레드가 sleep(n)을 호출하여 n ms동안 잠을 자는 상태
- BLOCK : 스레드가 I/O 작업 요청을 하면 JVM이 자동으로 BLOCK 상태로 만듦
- TERMINATED : 스레드 종료
스레드 생명 주기
스레드 종료와 다른 스레드 강제 종료
- 스스로 종료하는 경우 : run() 메소드 리턴
- 다른 스레드에서 강제 종료하는 경우 : interrupt() 메소드 사용
class TestThread extends Thread {
int n=0;
public void run() {
while(true) {
n++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
return; // 예외 처리로 종료
}
}
}
}
public class InterruptEx {
public static void main(Stirng[] args) {
TestThread thread = new TestThread();
thread.start();
thread.interrupt(); // 스레드 강제 종료
}
}
flag를 이용한 종료
- 스레드 안의 flag 변수를 이용하여 종료
class TestThread extends Thread {
int n=0;
bool flag = false; // false로 초기화
public void finish() {
flag = true;
}
public void run() {
while(true) {
n++;
try {
Thread.sleep(1000);
if (flag == true)
return;
} catch (InterruptException e) {
return;
}
}
}
}
public FlagEx {
public static void main(String[] args) {
TestThread thread = new TestThread();
thread.start();
tread.finish(); // TestThread 강제 종료
}
}
스레드 동기화 (Thread Synchronization)
- 멀티스레드 프로그램 작성 시 주의점
- 다수의 스레드가 공유 데이터에 동시에 접근하는 경우
- 공유 데이터의 값에 예상치 못한 결과 발생 가능 - 이를 해결하기 위해 스레드 동기화 사용
- 공유 데이터를 접근하는 모든 스레드를 관리
- 한 스레드가 공유 데이터에 접근하는 경우 작업이 끝날 때까지 다른 스레드들은 대기
synchronized
- 한 스레드가 독점적으로 실행해야 하는 부분을 표시하는 키워드
- synchronized 메소드
synchronized void add() {
int n = getCurrentSum();
n+=10;
setCurrentSum(n);
}
- synchronized 코드 블럭
void execute() {
...
synchronized(this) {
int n = getCurrentSum();
n+=10;
setCurrentSum(n);
}
...
}
wait(), notify(), notifyAll()
- 동기화 객체 : 2개 이상의 스레드 사이에 동기화 작업에 사용되는 객체
- 동기화 메소드
- synchronized 블록 내에서만 사용되어야 함
- wait()
- 다른 스레드가 notify()를 불러줄 때까지 대기
- notify()
- wait()을 호출하여 대기 중인 스레드를 깨우고 RUNNABLE 상태로 만듦
- 2개 이상의 스레드가 대기 중이어도 한 개의 스레드만 깨움
- notifyAll()
- wait()를 호출하여 대기 중인 모든 스레드를 깨우고 모두 RUNNABLE 상태로 만듦
이미지 출처 : 명품 JAVA 프로그래밍 (황기태, 김효수 저)