Java 스레드 풀에 대하여 글을 올리고 싶었으나...

이번 주말 중으로 올릴 수 있으면 올리도록 하겠습니다.

못 올릴 수도 있습니다!

 

올리기는 반드시 올릴거에요!

 

결국 주말에 올리지 못 했습니다....

(지금 하고 있는게 굉장히 바빠서..)

 

이번 달 안에 올릴 수 있을지 모르겠네요 ...

아 물론! 스레드 풀은 당연히 공부가 끝난 상태입니다.

 

올리긴 올리겠습니다..

 

 

이번에는 스레드 그룹에 대하여 알아 보도록 하겠습니다.

 

스레드 그룹의 정의는 다음과 같이 되어 있습니다.

 

- 스레드 그룹(ThreadGroup)은 관련된 스레드를 묶어서 관리할 목적으로 이용된다.

 

추가적인 설명은 다음과 같습니다.

 

JVM이 실행되면 system 스레드 그룹을 만들고, JVM 운영에 필요한 스레드들을 생성해서 system 스레드 그룹에 포함시킵니다.

system의 하위 스레드 그룹으로 main을 만들고 메인 스레드를 main 스레드 그룹에 포함시킵니다.

스레드는 반드시 하나의 스레드 그룹에 포함됩니다.

명시적으로 스레드 그룹에 포함시키지 않으면 기본적으로 자신을 생성한 스레드와 같은 스레드 그룹에 속하게 됩니다.

우리가 생성하는 작업 스레드는 대부분 main 스레드가 생성하므로 기본적으로 main 스레드가 생성하므로 기본적으로 main 스레드 그룹에 속하게 됩니다.

 

※ 추가 설명에서 main 스레드 또한 사용자가 직접 생성하기 때문에 유저 스레드라고 생각합니다.

ex) public static void main(String[] args) {}

 


스레드를 그룹 지정하여 관리하게 되면 여러가지 편할 것 같습니다(현업에서 많이 사용할지는 아직 잘 모르겠습니다.)

 

스레드 그룹이 있을 경우 관리를 위해 그룹 이름을 알아야 하지요!

그에 따른 스레드 그룹 이름 얻기.

 

스레드 그룹을 만들지도 않았는데 관리는 무슨!

스레드 그룹 생성.

 

다 사용 했으면 자원 돌려줘야지!

스레드 그룹의 일괄 interrupt().

 

이렇게 3가지에 대하여 알아 보도록 하겠습니다.

 


스레드 그룹의 이름을 얻기 위해서는 getName() 메소드를 이용하여 확인할 수 있습니다.

 

 

다음 예제를 통하여 알아 보도록 하겠습니다.

 

 

 

 

ThreadInfoExample.java - 현재 실행 중인 스레드 정보

package thread_group;
 
 
public class ThreadInfoExample {
 
    public static void main(String[] args) {
        AutoSaveThread autoSaveThread = new AutoSaveThread();
        autoSaveThread.setName("AutoSaveThread");
        autoSaveThread.setDaemon(true);
        autoSaveThread.start();
        
        Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
        Set<Thread> threads = map.keySet();
        for(Thread thread : threads) {
            System.out.println("Name: " + thread.getName() +
                    ((thread.isDaemon())?"(데몬)""(주)"));
            System.out.println("\t" + "소속그룹: " + thread.getThreadGroup().getName());
            System.out.println();
        }
    }
}

※ 이 코드에서 AutoSaveThread 클래스는 제 이전 글인 데몬 스레드 글에서 확인 하실 수 있습니다.

    또는 이것이 자바다 교재를 확인하시거나 이것이 자바다 교재에서 제공하는 예제코드를 활용하시기 바랍니다.

 

 

이 코드를 실행하시게 되면 다음과 같은 결과를 확인할 수 있습니다.

 

Name: AutoSaveThread(데몬)

소속그룹: main

 

Name: Reference Handler(데몬)

소속그룹: system

 

Name: Signal Dispatcher(데몬)

소속그룹: system

 

Name: main(주)

소속그룹: main

 

Name: Finalizer(데몬)

소속그룹: system

 


스레드 그룹 생성은 간단하게 생성 방법에 대해서만 알아보고, 스레드 그룹의 일괄 interrupt()에서 사용하며 익숙해지도록 하겠습니다.

 

스레드 그룹 생성은 다음과 같이 할 수 있습니다.

 


ThreadGroup tg = new ThreadGroup(String name);

ThreadGroup tg = new ThreadGroup(ThreadGroup parent, String name);


Thread t = new Thread(ThreadGroup group, Runnable target);

Thread t = new Thread(ThreadGroup group, Runnable target, String name);

Thread t = new Thread(ThreadGroup group, Runnable target, String name, long stackSize);

Thread t = new Thread(ThreadGroup group, String name);


이 6가지를 통해서 알 수 있는 것은 다음과 같습니다.

 

- 스레드 그룹 생성 시 부모(parent) 스레드 그룹을 지정하지 않으면 현재 스레드가 속한 그룹의 하위 그릅으로 생성됩니다.

ex) main 스레드가 ThreadGroup(String name)을 이용해 새로운 스레드 그룹 생성시, main 스레드 그룹의 하위 스레드 그룹이 됩니다.

- Runnable 타입의 target은 Runnable 구현 객체를 말합니다.

- String 타입의 name은 스레드의 이름입니다.

- long 타입의 stackSize는 JVM이 이 스레드에 할당할 stack 크기입니다.

 


스레드 그룹의 일괄 interrupt()

 

스레드 그룹을 생성하게 될 경우의 편리성! 이라 할 수 있는게 대표적으로 스레드 종료가 있습니다.

스레드 그룹을 지정하지 않고, interrupt() 하게 될 경우, 스레드마다 interrupt()를 작성해 줘야 합니다.

하지만! 스레드 그룹을 지정해서 관리할 경우, 같은 스레드 그룹에 소속되어 있을 경우 스레드 그룹의 interrupt() 메소드를 한 번만 호출해주면 됩니다.

interrupt()를 작성하지도 않았는데 이것이 가능한 이유는 스레드 그룹의 interrupt() 메소드는 포함된 모든 스레드의 interrupt() 메소드를 내부적으로 호출해주기 때문입니다.

 

글이 아닌 [그림]으로 조금 더 직관적으로 알아 보도록 하겠습니다.

 

[그림 - 스레드 그룹의 일괄 interrupt()]

또한 다음과 같은 점도 있습니다.

 

스레드 그룹의 interrupt() 메소드는 소속된 스레드의 interrupt() 메소드를 호출만 할 뿐 개별 스레드에서 발생하는 InterruptedException에 대한 예외 처리를 하지 않습니다. 그렇기 때문에 안전한 종료를 위해서는 개별 스레드에 대해서 예외 처리를 해야 합니다.

 

스레드 그룹에도 interrupt() 메소드 이외에 suspend(), resume(), stop() 메소드들이 있는데, 모두 Deprecated 되었습니다.

 

다음 표는 Thread Group이 가지고 있는 주요 메소드들입니다.

 

메소드 설명
int activeCount() 현재 그룹 및 하위 그룹에서 활동 중인 모든 스레드의 수를 리턴한다.
int activeGroupCount() 현재 그룹에서 활동 중인 모든 하위 그룹의 수를 리턴한다.
void checkAccess() 현재 스레드가 스레드 그룹을 변경할 권한이 있는지 체크한다. 만약 권한이 없으면 SecurityException을 발생시킨다.
void destroy() 현재 그룹 및 하위 그룹을 모두 삭제한다. 단, 그룹 내에 포함된 모든 스레드들이 종료 상태가 되어야 한다.
boolean isDestroyed() 현재 그룹이 삭제되었는지 여부를 리턴한다.
int getMaxPriority() 현재 그룹에 포함된 스레드가 가질 수 있는 최대 우선순위를 리턴한다.
void setMaxPriority(int pri) 현재 그룹에 포함된 스레드가 가질 수 있는 최대 우선순위를 설정한다.
String getName() 현재 그룹의 이름을 리턴한다.
ThreadGroup getParent() 현재 그룹의 부모 그룹을 리턴한다.
boolean parentOf(ThreadGroup g) 현재 그룹이 매개값으로 지정한 스레드 그룹의 부모인지 여부를 리턴한다.
boolean isDaemon() 현재 그룹이 데몬 그룹인지 여부를 리턴한다.
void setDaemon(boolean daemon) 현재 그룹을 데몬 그룹으로 설정한다.
void list() 현재 그룹에 포함된 스레드와 하위 그룹에 대한 정보를 출력한다.
void interrupt() 현재 그룹에 포함된 모든 스레드들을 interrupt한다.

 

많습니다...

다 외우시거나 외우고 계시는 분들이 많을 것 같습니다만, 저는 필요할 때마다 찾아서 사용하고 많이 사용하면서 익숙해지도록 해야겠네요.

 

이제 예제를 통하여 스레드 그룹의 interrupt()를 해보도록 하겠습니다.

다음의 예제는 스레드 그룹 생성 후, 정보를 출력합니다. 마지막으로 3초 후 스레드 그룹의 interrupt() 메소드를 호출해서 스레드 그룹에 포함된 모든 스레드들을 종료시킵니다.

 

 

 

 

WorkThread.java - InterruptedException이 발생할 때 스레드가 종료되도록 함

package thread_group_create;
 
public class WorkThread extends Thread {
 
    public WorkThread(ThreadGroup threadGroup, String threadName) {
        super(threadGroup, threadName);    // 스레드 그룹과 스레드 이름을 설정
    }
    
    @Override
    public void run() {
        while(true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                /*
                    InterruptedException이 발생될 때,
                    while문을 빠져나와 스레드를 종료시킵니다.
                */
                System.out.println(getName() + " interrupted");
                break;
            }
        }
        System.out.println(getName() + " 종료됨");
    }
}

 

 

 

 

ThreadGroupExample.java - 스레드 그룹을 이용한 일괄 종료 예제

package thread_group_create;
 
public class ThreadGroupExample {
 
    public static void main(String[] args) {
        //    myGroup에 두 스레드를 포함시킵니다.
        ThreadGroup myGroup = new ThreadGroup("myGroup");
        WorkThread workThreadA = new WorkThread(myGroup, "workThreadA");
        WorkThread workThreadB = new WorkThread(myGroup, "workThreadB");
        
        workThreadA.start();
        workThreadB.start();
        
        System.out.println("[ main 스레드 그룹의 list() 메소드 출력 내용 ]");
        ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();
        mainGroup.list();
        System.out.println();
        
        try { Thread.sleep(3000); } catch (InterruptedException e) {}
        
        System.out.println("[ myGroup 스레드 그룹의 interrupt() 메소드 호출 ]");
        myGroup.interrupt();
    }
}

 

 

이 예제를 실행할 경우 다음과 같은 결과를 확인할 수 있습니다.

 

[ main 스레드 그룹의 list() 메소드 출력 내용 ]

java.lang.ThreadGroup[name=main,maxpri=10]

    Thread[main,5,main]

    java.lang.ThreadGroup[name=myGroup,maxpri=10]

        Thread[workThreadA,5,myGroup]

        Thread[workThreadB,5,myGroup]

 

[ myGroup 스레드 그룹의 interrupt() 메소드 호출 ]

workThreadB interrupted

workThreadB 종료됨

workThreadA interrupted

workThreadA 종료됨

 

※ myGroup 스레드 그룹의 interrupt() 메소드 호출의 결과는 매 실행시 출력 순서가 달라질 수 있습니다.

 

 

ThreadGroupExample.java의 mainGroup.list() 에서 list() 메소드는 현재 스레드 그룹의 이름과 최대 우선순위를 헤더로 출력하고, 그 아래에 현재 스레드 그룹에 포함된 스레드와 하위 스레드 그룹의 내용을 보여줍니다.

스레드의 경우 [스레드 이름, 우선순위, 소속 그룹명]으로 출력되는 것을 확인할 수 있습니다.

 

또한 interrupt() 메소드를 호출하면 myGroup에 포함된 두 스레드에서 InterruptedException이 발생되어 스레드가 종료되는 것을 알 수 있습니다.

 

 

데몬 스레드에 대하여 알아보도록 하겠습니다.

 

일반적으로 데몬이라는 말은 리눅스나 유닉스등의 데몬 프로세스에서 많이 들어 보셨을 거라고 생각합니다.

ex) sshd, httpd - d는 daemon

 

daemon을 검색해보면 

멀티태스킹 운영체제에서 데몬은 사용자가 직접적으로 제어하지 않고, 백그라운드에서 돌면서 여러 작업을 하는 프로그램을 말한다.

라고 설명이 나와있습니다.

※ 정의 출처 : 데몬 (컴퓨팅) - 위키백과, 우리 모두의 백과사전

 

데몬 (컴퓨팅) - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 멀티태스킹 운영 체제에서 데몬(daemon, 발음: 데이먼/'deɪmən/ 또는 디먼 /'dimən/[1])은 사용자가 직접적으로 제어하지 않고, 백그라운드에서 돌면서 여러 작업을 하는 프로그램을 말한다. 시스템 로그를 남기는 syslogd처럼 보통 데몬을 뜻하는 ‘d’를 이름 끝에 달고 있으며, 일반적으로 프로세스로 실행된다. 데몬은 대개 부모 프로세스를 갖지 않으며, 즉 PPID가 1이며, 따라서 프로세스 트리에서 i

ko.wikipedia.org

그런데 자바의 데몬 스레드의 정의는 다음과 같이 명시 되어 있습니다(이것이 자바다 기준)

 

데몬(daemon) 스레드는 주 스레드의 작업을 돕는 보조적인 역할을 수행하는 스레드이다.

 

처음 데몬 정의에 나와있던 것을 바탕으로 생각해보면 데몬이라는 것은 syslogd 처럼 운영체제의 시스템 로그를 기록하는 작업을 돕는 역할을 수행하는 프로세스라고 생각할 수 있습니다.

 

마찬가지로 자바로 데몬 스레드도  프로그래밍을 해주면 우리가 일반적으로 알고 있는 데몬 프로세스처럼 사용할 수 있습니다.

 


데몬 스레드의 정의를 조금 더 알아보도록 하겠습니다.

 

1. 주 스레드의 작업을 돕는 보조적인 역할을 수행하는 스레드

2. 주 스레드가 종료되면 데몬 스레드는 강제적으로 자동 종료된다.(주 스레드의 보조 역할을 수행하므로 주 스레드가 종료되면 데몬 스레드의 존재 의미가 없어지기 때문)

 

이 2가지를 제외하면 데몬 스레드는 일반 스레드와 크게 차이가 없습니다.

 

데몬 스레드의 적용 예

 

1. 워드프로세서의 자동 저장

2. 미디어 플레이어의 동영상 및 음악 재생

 

등이 있는데, 이 기능들은 주 스레드(워드프로세스, 미디어 플레이어, JVM)가 종료되면 같이 종료됩니다.

 


데몬 스레드를 만들어 보도록 하겠습니다.

 

스레드를 데몬으로 만들기 위해서는 주 스레드가 데몬이 될 스레드의 setDaemon(true)를 호출해 주면 됩니다.

 

주의점은 스레드를 실행하는 start() 메소드가 호출되고 나서 setDaemon(true)를 호출하면 IllegalThreadStateException이 발생하기 때문에 start() 메소드 호출 전에 setDaemon(true)를 호출해야 합니다.

 

또한 현재 실행 중인 스레드가 데몬 스레드인지 아닌지를 구별하는 방법은 isDaemon() 메소드의 리턴값을 조사해보면 됩니다.

 

데몬 스레드일 경우 true를 리턴합니다.

 

 

예제를 통해 직접 확인해 보도록 하겠습니다.

 

 

 

 

 

AutoSaveThread.java - 1초 주기로 save() 메소드를 호출하는 데몬 스레드

package thread_state_control_4;
 
public class AutoSaveThread extends Thread {
 
    public void save() {
        System.out.println("작업 내용을 저장함.");
    }
    
    @Override
    public void run() {
        while(true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                break;
            }
            save();
        }
    }
}

 

 

 

DaemonExample.java - 메인 스레드가 실행하는 코드

 

package thread_state_control_4;
 
public class DaemonExample {
 
    public static void main(String[] args) {
        AutoSaveThread autoSaveThread = new AutoSaveThread();
        autoSaveThread.setDaemon(true);        //    AutoSaveThread를 데몬 스레드로 만듬
        autoSaveThread.start();
        
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
        }
        
        System.out.println("메인 스레드 종료");
    }
}

 

 

이 예제를 실행하게 되면 다음과 같은 결과를 확인할 수 있습니다.

 

작업 내용을 저장함.

작업 내용을 저장함.

메인 스레드 종료

 

교재에서는 "작업 내용을 저장함." 이 3번 출력된 결과를 보였는데 저는 2번 출력 되었습니다.

 


※ 간단하게 알아 보았지만 추후 추가 할 예제 등이 있으면 추가 하도록 하겠습니다.

 

 

+ Recent posts