Language/Java

12.1 쓰레드 (Thread) - 구현

리져니 2021. 7. 28. 20:06

쓰레드를 구현하는 방법

1. Thread 클래스 상속

* 다른 클래스를 상속 받을수가 없어서 일반적으로 Runnable 인터페이스를 구현하는 방법을 쓴다.

Class MyThread extends Thread{
	public void run() { ... }	// Thread클래스의 run() 오버라이딩
}


Class Main{
	public static void main(String[] args){
    	MyThread t1 = new MyThread();	// Thread의 자손 클래스의 인스턴스 생성
        
        t1.start();		// 쓰레드 실행
    }
}

Thread클래스르 상속받으면, 자손 클래스에서 조상인 Thread클래스의 메서드를 직접 호출할 수 있다.

 

 

2. Runnable 인터페이스 구현

Class MyThread implements Runnable{
	public void run() { ... }	// Runnable인터페이스의 run() 구현
}

Class Main{
	public static void main(String[] args){
    	Runnable r = new MyThread();
        Thread t2 = new Thread(r);	// 생성자 Thread(Runnable target)
        
        // 위의 두줄을 아래와 같이 할수도 있다
        Thread t2 = new Thread(new MyThread(r));
        
        t2.start();	// 쓰레드 실행
    }
}

* Runnable 인터페이스: 오직 run()만 정의되어 있음

Runnable 인터페이스를 구현한 클래스의 인터페이스를 생성한 다음, 이 인스턴스를 Thread클래스의 생성자 매개변수로 제공해야 한다.

Thread 클래스의 메서드

Runnable 인터페이스를 구현하면 Thread클래스의 static메서드인 currentThread()를 호출하여 쓰레드에 대한 참조를 얻어와야만 Thread클래스의 메서드를 호출할 수 있다.

Thread.currentThread().getName();

 

쓰레드의 실행

 t2.start();	// 쓰레드 실행

start() 호출을 통해 쓰레드를 실행 할 수 있다. start()가 호출된뒤 바로 실행이 되는 것이 아니라, 일단 대기 상태에 있다가 차례가 되면 실행된다.

한번 실행이 종료된 쓰레드는 다시 실행할수 없기 때문에 하나의 쓰레드에 대해 start()는 한번만 호출될 수 있다.

(두번 이상 호출시 IllegalThredStateException이 발생함)

 

run() vs start()

run()을 호출 하는 것은 생성된 쓰레드를 실행시키는 것이 아니라 단순히 클래스에 선언된 메서드(run())를 호출하는 것이다. 반면에 start()는 새로운 쓰레드가 작업을 실행하는데 필요한 호출스택을 생성한 다음에 run()을 호출해서 생성된 호출스택에 run()이 첫번째로 올라가게 한다.

 

 

쓰레드의 우선순위

쓰레드는 우선순위(priority)라는 속성(멤버변수)을 가지고 있는데, 쓰레드가 수행하는 작업의 중요도에 따라 쓰레드의 우선순위를 서로 다르게 지정하여 특정 쓰레드가 더 많은 작업시간을 갖도록 할 수 있다.

 

우선 순위 지정

쓰레드의 우선순위와 관련된 메서드와 상수

쓰레드가 가질 수 있는 우선순위의 범위는 1~10이며 숫자가 높을수록 우선순위가 높다.

** 쓰레드의 우선순위는 쓰레드를 생성한 쓰레드로부터 상속 받는다.

 

class Main{
	public static void main(String[] args){
    	MyThread t1 = new MyThread();
            
        t1.setPriority(7);	// 쓰레드 t1의 우선순위를 7로 변경
        System.out.println(t1.getPriority());	// 7
        t1.start();		// 쓰레드 t1 실행
    }
}

 

싱글 코어 vs 멀티 코어에서의 쓰레드 우선순위

1. 싱글 코어

우선 순위가 같은 경우 각 쓰레드에게 거의 같은 양의 실행시간이 주어지지만, 우선 순위가 다르면 우선 순위가 높은 쓰레드에게 상대적으로 많은 양의 실행시간이 주어지고 결과적으로 작업이 빨이 완료된다.

 

2. 멀티 코어

멀티 코어에서는 쓰레드의 우선순위에 따른 차이가 거의 없다. 

 

* 멀티코어라 해도 OS마다 다른 방식으로 스케쥴링하기 때문에 어떤 OS에서 실행하느냐에 따라 다른 결과를 얻을 수 있다.

 

 

쓰레드 그룹(Thread group)

서로 관련된 스레드를 그룹으로 다루기 위한것. 쓰레드 그룹을 생성해서 쓰레드를 그룹으로 묶어서 관리할 수 있다.

보안상의 이유로 도입되었으며, 자신이 속한 쓰레드 그룹이나 하위 쓰레드 그룹은 변경할 수 있지만 다른 쓰레드 그룹의 쓰레드를 변경할 수는 없다.

ThreadGroup(String name) 지종된 이름의 새로운 쓰레드 그룹 생성
ThreadGroup(ThreadGroup parent, String name) 지정된 쓰레드 그룹에 포함되는 새로운 쓰레드 그룹 생성
int activeCount() 쓰레드 그룹에 포함된 활성
int activeGroupCount() 쓰레드 그룹에 포함된 활성상태에 있는 쓰레드 그룹의 수 반환
void checkAccess() 현재 실행중인 쓰레드가 쓰레드 그룹을 변경할 권한이 있는지 체크
void destroy() 쓰레드 그룹과 쓰레드가 쓰레드 그룹을 변경할 권한이 있는지 체크
int eunmerate(Thread[] list)
int eunmerate(Thread[] list, boolean recurse)
int eunmerate(ThreadGroup[] list)
int eunmerate(ThreadGroup[] list, boolean recurse)
쓰레드 그룹에 속한 쓰레드 또는 하위 쓰레드 그룹의 목록을 지정된 배열에 담고 그 개수를 반환
recurse의 값을 true로 하면 쓰레드 그룹에 속한 하위 쓰레드 그룹에 쓰레드 또는 쓰레드 그룹까지 배열에 담음
int getMaxPriority() 쓰레드 그룹의 최대 우선순위를 반환
String getName() 쓰레드 그룹의 이름 반환
ThreadGroup getParent() 쓰레드 그룹의 상위 쓰레드 그룹을 반환
void interrupt() 쓰레드 그룹에 속한 모든 쓰레드를 interrupt
boolean isDaemon() 쓰레드 그룹이 데몬 쓰레드 그룹인지 확인
boolean isDestroyed() 쓰레드 그룹이  삭제되었는지 확인
void list() 쓰레드 그룹에 속한 쓰레드와 하위 쓰레드 그룹에 대한 정보를 출력
boolean parentOf(ThreadGroup g) 지정된 쓰레드 그룹의 상위 쓰레드 그룹인지 확인
void setDaemon(boolean daemon) 쓰레드 그룹을 데몬 쓰레드 그룹으로 설정(true)/해제(false)
void setMaxPriority(int pri) 쓰레드 그룹의 최대 우선순위 설정

 

쓰레드를 쓰레드 그룹에 포함시키려면 Thread의 생성자를 이용해야 한다.

모든 쓰레드는 반드시 쓰레드 그룹에 포함되어 있어야 하기 때문에, 쓰레드 그룹을 지정하는 생성자를 사용하지 않은 쓰렏는 자신을 생성한 쓰레드와 같은 쓰레드 그룹에 속하게 된다.

 

Thread의 쓰레드 그룹과 관련된 메서드는 다음과 같다.

ThreadGroup getThreadGroup() 쓰레드 자신이 속한 쓰레드 그룹을 반환
void uncaughtException(Thread t, Throwable e) 쓰레드 그룹의 쓰레드가 처리되지 않은 예외에 의해 실행이 종료되었을 때, JVM에 의해 이 메서드가 자동적으로 호출된다

 

 

쓰레드는 '사용자 쓰레드'와 '데몬 쓰레드'가 있다.

데몬 쓰레드(daemon thread)

다른 일반 쓰레드의 작업을 돕는 보조적인 역할을 수행하는 쓰레드로, 일반 쓰레드가 모두 종료되는 데몬 쓰레드는 강제적으로 자동 종료된다.

데몬 쓰레드는 무한루프와 조건문을 이용해서 실행 후 대기하고 있다가 특정 조건이 만족되면 작업을 수행하고 다시 대기하도록 작성한다.

일반 쓰레드의 작성방법과 실행방법이 같으며, 쓰레드를 생성한 다음 실행을 하기 전에 setDaemon(true)를 호출하면 된다. 데몬 쓰레드가 생성한 쓰레드는 자동적으로 데몬 쓰레드가 된다.

Thread t = new Thread(new MyThread());

t.setDaemon(true);	// 이 부분이 없으면 종료되지 않는다.
t.start();	//setDaemon() 호출 후에 실행되어야 한다. 그렇지 않으면 예외가 발생함.

for(int i=1; i<=10; i++){
	try{
    	Thread.sleep(1000);
    } catch(InterruptedException e){
    	System.out.println(i);
    }   
    if(i==5)
    	autoSave = true;
    }
    System.out.println("프로그램 종료.");
}

3초마다 변수 autoSave의 값을 확인해서 그 값이 true이면 autoSave()를 호출하는 작업을 무한히 반복하도록 쓰레드를 작성 했을때, 쓰레드를 데몬 쓰레드로 설정하지 않으면 프로그램은 강제종료를 하지 않는 한 영원히 종료되지 않는다.

 

728x90