본문 바로가기

개념 정리/Java

[JAVA] 스레드


  1. 스레드와 멀티스레딩
  2. 스레드 만들기
  3. 스레드 상태, 우선순위
  4. 스레드 종료
  5. 스레드 동기화

1. 스레드와 멀티스레딩

- 스레드 : 독립적으로 작업을 실행하는 단위. 한 스레드로 하나의 작업만 처리 가능. 응용프로그램 내의 자원과 메모리 공유.
- 멀티스레딩 : 작업 개수만큼 스레드 생성.  다수의 스레드를 가지고 다수의 작업을 동시 처리. 한 스레드가 대기하는 동안 다른 스레드 실행. 

 

[JVM과 멀티스레드]

하나의 JVM(자바가상기계)는 하나의 자바 응용프로그램만 실행.

각 자바 응용프로그램은 별개의 메모리 영역에서 독립적으로 실행되고, 하나 이상의 스레드를 가질 수 있음(멀티스레딩).

2개 이상의 자바 응용프로그램이 서로 정보 주고받으려면 소켓통신같은 통신방법 이용.

 

 

 

2. 스레드 만들기

- Thread 클래스 이용하는 방법
- Runnable 인터페이스 이용하는 방법 (일반적)

 

[Thread 클래스 메소드]

Thread 클래스 경로명 : java.lang.Thread  (java.lang패키지이므로 import 안해도 자동 import됨)

 

 

 

[Thread 클래스로 스레드 만들기]

 

1. 스레드 클래스 상속 & run() 코드 작성

- Thread 클래스 상속받아 클래스 작성

- run()메소드에 작성된 코드 : 스레드 코드

- 스레드는 run()부터 실행 시작. run() 종료시 스레드 종료됨

class TimerThread extends Thread {	// Thread 클래스 상속
	@Override
    public void run(){	// run() 오버라이딩
    	...
    }
}

 

2. 스레드 객체 생성

TimerThread th = new TimerThread();	// 스레드 객체 생성

 

3. 스레드 시작

start() 메소드 호출 → JVM에 의해 run()메소드 호출 (실행시작)

th.start();

 

4. 전체코드:

class TimerThread extends Thread {	// Thread 클래스 상속
    int n = 0;
    @Override
    public void run() {	// run() 오버라이딩
    	while(true) {
            System.out.println(n);
            n++;
            try {
                Thread.sleep(1000);	// 1초(1000ms)동안 잠자기
            } catch(InterruptedException e) { return; }
        }
    }
}

public class TestThread {
    public static void main(String args[]) {
    	TimerThread th = new TimerThread();
        th.start();
    }
}

* sleep()하는 동안 JVM은 다른 스레드 실행시킴

* sleep()하는 동안 발생할 예외처리 블록 가지고 있어야 함 (try-catch 블록 필수)

 

 

[Runnable 인터페이스로 스레드 만들기]

 

// Runnable 인터페이스
interface Runnable {
    public void run();	// 추상메소드 run()하나만 가짐
}

 

1. 스레드 클래스 선언 & run() 코드 작성

class TimerRunnable implements Runnable {	// Runnable 인터페이스 구현
    @Override
    public void run() {	// run() 오버라이딩
        ...
    }
}

 

2. 스레드 객체 생성

TimerRunnable 객체는 스레드 코드로 작동할 run()이 구현된 객체, th는 스레드 객체.

스레드 객체 생성시 TimerRunnable 객체 전달함

Thread th = new Thread(new TimerRunnable());	// 스레드 객체 생성

 

 

3. 스레드 시작

th.start();

 

4. 전체 코드:

class TimerRunnable implements Runnable {	// Runnable 인터페이스 구현
    int n = 0;
    @Override
    public void run() {	// run() 오버라이딩
    	while(true) {
            System.out.println(n);
            n++;
            try {
                Thread.sleep(1000);	// 1초(1000ms)동안 잠자기
            } catch(InterruptedException e) { return; }
        }
    }
}

public class TestRunnable {
    public static void main(String args[]) {
    	Thread th = new Thread(new TimerRunnable());
        th.start();
    }
}

 

 

 

 

3. 스레드 상태, 우선순위

[스레드 상태]

· NEW : 스레드 탄생. 실행준비안됨

· RUNNABLE : 스레드가 실행준비되어 스케줄링 기다리는 상태이거나, 실행중인 상태

· TIMED_WAITING : 스레드가 sleep() 호출해서 잠 자는 상태

· BLOCK : 스레드가 I/O작업 실행해서 I/O작업 완료 기다리면서 멈춘 상태.

· WAITING : 스레드가 wait()을 호출해서, 다른 스레드가 notify()를 부를 때까지 무한정 대기

· TERMINATED : 스레드 종료 상태

 

 

 

 

[스레드 우선순위]

스레드 우선순위 체계:

· 최댓값 = 10

·  최솟값 = 1

·  보통값 = 5 (main 스레드)

 

우선순위 값 바꾸려면 setPriority() 메소드 이용

void setPriority(int newPriority)	// newPriority로 스레드의 우선순위 값 변경

 

4. 스레드 종료

[스스로 종료]

run() 메소드가 리턴 시 종료

public void run() {
    ...
    return;	// 스레드 스스로 종료
    ...
}

 

[강제 종료]

한 스레드에서 interrupt() 메소드 호출해 다른 스레드 강제 종료시키기

* interrupt를 받는 스레드의 catch 블록에 return;이 있어야 종료됨

public static void main(String args[]) {
    TimerThread th = new TimerThread();
    th.start();
    
    th.interrupt();	// TimerThread 강제 종료
}

 

[flag 이용한 강제 종료]

finish() 메소드와 flag 필드를 만들고, 한 스레드에서 finish() 메소드 호출해 다른 스레드 강제 종료시키기

class TimerThread extends Thread {
    private boolean flag = false;		// false로 초기화
    public void finish() { flag = true; }	// finish 메소드 생성
    @Override
    public void run() {
        while(true) {
        ...
        if(flag == true) return;	// flag가 true이면 스레드 종료
        ...
        }
    }
 }
 public static void main(String args[]){
     ...
     th.finish();	// TimerThread 강제 종료
 }

 

5. 스레드 동기화

· synchronized : 동기화 블록 지정
· wait() - notify() : 스레드 실행 순서 제어

 

[ synchronized 키워드 ]

순차 실행 제어

임계구역 지정. 다른 스레드는 lock 풀릴 때까지 대기.

 

↓ 한 스레드가 임계영역 블록 실행을 마칠 때까지 다른 스레드는 대기해야 함.

// 메소드 전체를 임계구역으로
synchronized void add(){
    ...
}


// 코드 블록을 임계구역으로
void add() {
    synchronized(this) {	// this 대신 다른 객체의 레퍼런스 사용가능
        ...
    }
}

 

[ wait() - nodify() ]

- producer - consumer 문제 해결.

- 한 스레드가 공유데이터에 대해 lock을 소유하고 있으면, 1) 공유데이터에 접근하려는 다른 스레드는 wait()를 호출해 대기하고, 2) 현재 lock을 소유한 스레드가 작업을 마친뒤 notify()를 호출해서 3) 대기중인 스레드를 깨워준다.

 

· wait() : 다른 스레드가 notify() 불러줄 때까지 대기

· notify() : 대기중인 스레드 깨워 RUNNABLE 상태로. 오직 한 개의 스레드만 깨움

· notifyAll() : 대기중인 모든 스래드 깨워 RUNNABLE 상태로.

 

* 위 메소드를 호출하는 코드는 synchronized로 지정돼있어야 함.

synchronized public void fill() {	// synchronized 키워드
    if(barSize == maxBarSize) {
        try {
            wait();	// 조건을 만족하면 대기
        } catch(InterruptedException e) {return;}
    }
	...
    notify();	// 기다리는 스레드 깨우기
}

 

 

명품 JAVA Programming 참고