JAVA/쓰레드

[JAVA 쓰레드 #5] 데몬 쓰레드

우엥우아앙 2021. 2. 3. 11:09

데몬 쓰레드

 

데몬쓰레드는 다른 User 쓰레드의 작업을 돕는 보조적인 역할을 수행하도록 되어 있다. 그래서 핵심적인 특징은 다른User 쓰레드가 모두 종료되면 데몬 쓰레드가 모두 종료되면 데몬 쓰레드는 강제적으로 종료되어진다. 메인 쓰레드는 당연히도 User 쓰레드이다.

 

1. 일반 쓰레드의 경우

일반 쓰레드에서 쓰레드를 데몬 쓰레드로 지정하는 방법은 setDaemon() 메소드이다. setDaemon(true)후에 start()를 시키면 된다.

package S_20210203;

import java.util.concurrent.TimeUnit;

public class DaemonThreads{
	public static void main(String[] args) throws Exception {
		String currentThreadName = Thread.currentThread().getName();
		System.out.println("["+currentThreadName+"] Thread Starts ...");

		Thread t1 = new Thread(new MyRunnableTask2(100),"Thread-1");
		Thread t2 = new Thread(new MyRunnableTask2(500),"Thread-2");
		
		t2.setDaemon(true);
		
		t1.start();
		t2.start();
		
		// 해당 쓰레드가 실행하고, 종료될까지 기다린다.
//		t1.join();
//		t2.join();
		
		System.out.println("["+currentThreadName+"] Thread Ends ...");
	}
}

class MyRunnableTask2 implements Runnable{
	private static int count = 0;
	private int id;
	private String taskId;
	private long sleepTime;
	
	@Override
	public void run() {
		boolean isRunningInDaemonThread = Thread.currentThread().isDaemon();
		String threadType = isRunningInDaemonThread ? "DAEMON" : "USER";
		String currentThreadName = Thread.currentThread().getName();
		System.out.println("#### <" + currentThreadName +","+ threadType + "," + taskId + "> starting...####");
		
		for(int i = 0; i<10; i++) {
			System.out.println("<" + currentThreadName +","+ threadType + "," + taskId + "> TICK TICK" + i);
			try {
				TimeUnit.MILLISECONDS.sleep(sleepTime);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}		
		System.out.println("#### <" + currentThreadName +","+ threadType + "," + taskId + "> done...####");
	}
	
	public MyRunnableTask2(long sleepTime) {
		this.id = ++count;
		this.taskId = "MyRunnableTask" + id;
		this.sleepTime = sleepTime;
	}
}

위와 같이 빨리 수행되는 t1과 늦게 수행되는 t2를 구동하면서 t2를 데몬쓰레드로 지정하면 결과가 다음과 같이 된다.

Main thread와 t1이 종료하면 t2는 더이상 진행되지 않고 즉시 종료된다.

 

그러나 주석처리를 한 join() 을 처리하면 결과는 아래와 같다

 

2. Executor의 경우

Executor의 경우 setDaemon()을 동일하게 사용한다. 단지 언제 호출을 하느냐가 문제인데 이것은 이전에 살펴본 ThreadFactory 인터페이스를 통해 구현한다. 이전 쓰레드 이름을 주기 위해 NameThreadsFactory 클래스를 구현했는데 이것을 상속한 DaemonThreadsFactory를 이용해 쓰레드을 데몬으로 설정하는 것을 알아보면

package S_20210203;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class DaemonThreadsUsingExecutors{
	public static void main(String[] args) throws Exception {
		String currentThreadName = Thread.currentThread().getName();
		System.out.println("["+currentThreadName+"] Thread Starts ...");
		
		ExecutorService execService = Executors.newCachedThreadPool( new DaemoneThreadsFactory()); 

		execService.execute(new MyRunnableTask2(100));
		execService.execute(new MyRunnableTask2(400));	
		
		execService.shutdown();
		
		System.out.println("["+currentThreadName+"] Thread Ends ...");
	}
}

class NamedThreadsFactory implements ThreadFactory {
	
	private static int count = 0;
	private static String Name = "MyThread-";

	@Override
	public Thread newThread(Runnable r) {
		return new Thread(r, Name + ++count);
	}
}

class DaemoneThreadsFactory extends NamedThreadsFactory {

	private static int count = 0;

	@Override
	public Thread newThread(Runnable r) {
		Thread t = super.newThread(r);
		count++;
		if(count%2 == 0){
			t.setDaemon(true); //setDaemon()
		}
		return t;
	}
}

짝수 쓰레드를 데몬으로 만들었는데, 이 경우도 역시 Main 쓰레드와 User 쓰레드가 종료되면 데몬 쓰레드는 더 이상 진행하지 않고 즉시 종료된다.