티스토리 뷰

9주차 : 멀티쓰레드 프로그래밍

Thread 클래스와 Runnable 인터페이스

쓰레드를 구현하는 방법은 Thread 클래스를 상속받는 방법과 Runnable 인터페이스를 구현하는 방법, 두 가지가 있다. Thread를 상속받으면 다른 클래스를 상속받을 수 없기 때문에, Runnable 인터페이스를 구현하는 방법이 일반적이다.

public interface Runnable{
    public abstract void run();
}

Runnable 인터페이스는 run()만 정의되어 있는 간단한 인터페이스(함수형 인터페이스)이다.

실행 방식 차이

public class ThreadEx1 {
    public static void main(String[] args) {
        ThreadEx_1_1 t1 = new ThreadEx_1_1();

        Runnable r = new ThreadEx1_2();
        Thread t2 = new Thread(r);

        t1.start();
        t2.start();
    }
}

class ThreadEx_1_1 extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(getName());
        }
    }
}

class ThreadEx1_2 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName());
        }
    }
}

Runnable 의 경우, Runnable을 구현한 클래스의 인스턴스를 생성한 후 , Thread 클래스의 생성자로 해당 인스턴스를 넘겨주는 방식으로 쓰레드를 만든다.

start() vs run()

  • run()
    • 단순히 클래스에 선언된 메서드 호출
  • start()
    • 새로운 쓰레드를 위한 call stack을 생성한 후에 run()을 호출

쓰레드의 상태

  • NEW : 쓰레드가 생성되고 아직 start() 가 호출되지 않은 상태
  • RUNNABLE : 실행 중 혹은 실행 가능한 상태
  • BLOCKED : 동기화 블럭에 의해 일시정지된 상태 (lock이 풀릴 때까지 기다리는 상태)
  • WAITING, TIMED_WAITING : 쓰레드 작업이 종료되지는 않았지만 실행가능하지 않은 (unrunnable) 일시정지 상태
  • TERMINATED : 쓰레드의 작업이 종료된 상태

쓰레드의 상태는 getState() 를 통해 확인할 수 있음. (JDK1.5 부터)

쓰레드 메소드

interrupt()

void interrupt() : interrupted 값을 false -> true로 변경
boolean isInterrupted() : interrupted 값을 반환
static boolean interrupted() : interrupted 값 반환 후 false로 변경

sleep(), wait(), join() 에 의해 일시정지(WAITING) 상태인 쓰레드에 대해 interrupt() 를 호출하면 sleep(), wait(), join() 애서 InterruptedException이 발생하고 쓰레드는 RUNNABLE 상태로 바뀐다.

yield()

쓰레드 자신에게 주어진 실행시간을 다음 차례의 쓰레드에게 양보한다.

join()

쓰레드 자신이 하던 작업을 잠시 멈추고 다른 쓰레드가 지정된 시간동안 작업을 수행하도록 할 때 join() 을 사용한다.

void join() : 시간을 지정하지 않으면 해당 쓰레드가 작업을 모두 마칠 때까지 대기
void join(long millis) 
void join(long millis, int nanos)

쓰레드의 우선순위

  • 쓰레드는 우선순위(priority) 라는 멤버 변수를 갖는다.
    • 우선순위 값에 따라서 쓰레드가 얻는 실행 시간이 달라진다. 즉, 특정 쓰레드가 더 많은 작업시간을 갖도록 설정할 수 있다.
    • 우선순위 범위
      • 가장 낮은 우선순위 : 1
      • 가장 높은 우선순위 :10
      • main thread 의 우선순위 : 5
  • 멀티코어 CPU에서는 쓰레드 우선순위에 따른 차이가 거의 없음.
    • 또한, OS의 스케쥴링 정책, JVM 구현 방식에 따라 실행 결과가 달라짐
    • 따라서, 어떤 일을 우선적으로 처리해야 한다면 쓰레드가 아닌 작업에 우선순위를 두어서 PriorityQueue에 넣는 방식이 나을 수 있음.

public class ThreadEx8 {
    public static void main(String[] args) {
        ThreadEx8_1 t1 = new ThreadEx8_1();
        ThreadEx8_2 t2 = new ThreadEx8_2();

        t2.setPriority(7);

        System.out.println("t1.getPriority() = " + t1.getPriority());
        System.out.println("t2.getPriority() = " + t2.getPriority());
        t1.start();
        t2.start();
    }
}

class ThreadEx8_1 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 300; i++) {
            System.out.print("-");
            for (int j = 0; j < 10000000; j++) {
            }
        }
    }
}

class ThreadEx8_2 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 300; i++) {
            System.out.print("|");
            for (int j = 0; j < 10000000; j++) {
            }
        }
    }
}

Main 쓰레드

  • 프로그램을 실행하면 하나의 쓰레드를 생성하고, 이 쓰레드가 main메서드를 호출해서 우리가 구현한 어플리케이션 작업이 실행된다.
  • main 쓰레드가 수행을 끝냈더라도 만약 다른 쓰레드가 작업을 마치지 않은 상태라면 프로그램이 종료되지 않는다. 즉, 실행 중인 사용자 쓰레드가 하나도 없을 때 프로그램이 종료된다.

데몬 쓰레드(daemon thread)

  • 일반 쓰레드(데몬 쓰레드가 아닌 쓰레드)의 작업을 돕는 역할을 수행
  • 일반 쓰레드가 모두 종료되면 데몬 쓰레드 종료
  • e.g. 가비지 컬렉터, 워드 프로세서 자동저장
  • 일반 쓰레드와 작성방법 / 실행 방법 동일
    • 쓰레드 실행 이전에 setDaemon(true) 를 호출하면 됨
public class ThreadEx10 implements Runnable{
    static boolean autoSave = false;

    public static void main(String[] args) {

        Thread t = new Thread(new ThreadEx10());
        t.setDaemon(true); //이 코드가 없으면 프로그램이 종료되지 않음
        t.start();

        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {

            }
            System.out.println("i = " + i);

            if (i == 5) {
                autoSave = true;
            }
        }
        System.out.println("프로그램을 종료합니다.");
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(3 * 1000);
            } catch (InterruptedException e) {
            }

            if (autoSave) {
                autoSave();
            }
        }
    }

    private void autoSave() {
        System.out.println("작업파일이 자동 저장되었습니다.");
    }
}

i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
작업파일이 자동 저장되었습니다.
i = 8
i = 9
프로그램을 종료합니다.

동기화

데드락

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함