본문 바로가기
Old Posts/Java

[Java] 스레드 그룹(Thread Group)

by A6K 2022. 2. 26.

JVM에서 생성되는 스레드들은 모두 어떤 스레드 그룹(Thread Group)에 속해 있다. 스레드 그룹은 연관되어 있는 스레드들을 묶어서 관리하기 위해 사용된다.

JVM이 시작되면 system 스레드 그룹이 생성된다. GC를 담당하는 Finalizer 스레드를 비롯하여 JVM 운영에 필요한 몇 가지 스레드들이 생성되어 system 그룹에 포함된다. 이후 system 스레드 그룹의 하위 그룹으로 main 스레드 그룹이 생성되고, main 메서드를 실행하는 main 스레드가 포함된다.

새로운 스레드를 생성할 때, 스레드 그룹을 지정할 수 있다. 만약 스레드가 포함될 스레드 그룹을 지정하지 않았다면 스레드를 생성하는 스레드가 포함된 스레드 그룹에 기본적으로 속하게 된다.

스레드 그룹 확인

특정 스레드가 어떤 그룹에 속해있는지를 알기 위해서는 'getThreadGroup()' 메서드를 사용하면 된다. 

public static void main(String[] args) {

    Thread t1 = new Thread();
    System.out.println("main : " + Thread.currentThread().getThreadGroup().getName());
    System.out.println("t1 :" + t1.getThreadGroup().getName());
}

메인 메서드에서 스레드를 하나 생성하자. 그리고 메인 메서드를 실행하는 현재 스레드와 새로 생성된 스레드의 그룹 이름을 찍어보자.

main : main
t1 :main

Process finished with exit code 0

앞서 메인 메서드를 실행하는 스레드는 main 스레드 그룹에 속한다고 했다. 또 한 별도로 스레드 그룹을 지정하지 않고 생성된 스레드는 새 스레드를 생성하는 스레드의 그룹에 속하게 된다고 했다. 위 코드의 실행 결과에서 이를 확인할 수 있다.

JVM에서 구동중인 몇 가지 스레드들의 그룹 정보를 알아보자. 

public static void main(String[] args) {

    for (Thread t : Thread.getAllStackTraces().keySet()) {
        System.out.println(t.getName() + ":" + t.getThreadGroup().getName());
    }
}

getAllStackTraces() 메서드가 리턴하는 Map 객체에서 keySet()에 해당하는 객체가 스레드 객체다. 이를 찍어보면,

Attach Listener:system
Monitor Ctrl-Break:main
Signal Dispatcher:system
Finalizer:system
Reference Handler:system
main:main

시스템과 main 스레드 그룹이 섞여 있는걸 볼 수 있다.

스레드 그룹 생성

ThreadGroup 객체를 이용해서 새로운 스레드 그룹을 생성할 수 있다. 이 때, 부모 스레드 그룹을 지정하지 않으면 현재 실행중인 스레드가 속한 스레드 그룹이 부모로 설정된다.

public static void main(String[] args) {

    ThreadGroup g1 = new ThreadGroup("Group1");

    ThreadGroup system = Thread.currentThread().getThreadGroup().getParent();
    ThreadGroup g2 = new ThreadGroup(system, "Group2");

    Thread t1 = new Thread(g1, "Thread 1");
    Thread t2 = new Thread(g2, "Thread 2");

    System.out.println("Group of t1 : " + t1.getThreadGroup().getName());
    System.out.println("Parent group of t1`s : " + t1.getThreadGroup().getParent().getName());
    System.out.println("Group of t2 : " + t2.getThreadGroup().getName());
    System.out.println("Parent group of t2`s : " + t2.getThreadGroup().getParent().getName());
}

이를 실행하면,

Group of t1 : Group1
Parent group of t1`s : main
Group of t2 : Group2
Parent group of t2`s : system

Process finished with exit code 0

이런 결과를 얻을 수 있다.

스레드 그룹 Interrupt

스레드 그룹은 그룹에 속한 스레드들을 관리하기 위해 만들어졌다고 했다. 그 중 하나가 스레드들에 일괄적으로 interrupt를 걸 수 있다는 점이 있다.

다음 예제를 보자.

public class TestThreadGroup {

    public static class WorkerThread extends Thread {
        public WorkerThread(ThreadGroup group, String name) {
            super(group, name);
        }

        @Override
        public void run() {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    break;
                }
            }

            System.out.println(Thread.currentThread().getName() + " Interrupted!!");
        }
    }

    public static void main(String[] args) {
        ThreadGroup group = new ThreadGroup("New thread group");

        Thread t1 = new WorkerThread(group, "Thread1");
        Thread t2 = new WorkerThread(group, "Thread2");
        Thread t3 = new WorkerThread(group, "Thread3");

        t1.start();
        t2.start();
        t3.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            /* No-op */
        }

        System.out.println("Call interrupt");
        
        // 일괄 Interrupt
        group.interrupt();
        try {
            t1.join();
            t2.join();
            t3.join();
        } catch (InterruptedException e) {
            /* No-op */
        }
        System.out.println("Finished");
    }
}

1초씩 Sleep만 하는 스레드를 정의하고, 스레드 그룹에 3개를 생성했다.

이후 ThreadGroup 객체에 interrupt() 메서드를 호출해서 그룹에 속해있는 모든 스레드에 interrupt를 날렸다. 그리고 스레드들이 끝나길 기다렸다가 종료한다.

이를 실행하면,

Call interrupt
Thread2 Interrupted!!
Thread1 Interrupted!!
Thread3 Interrupted!!
Finished

Process finished with exit code 0

특정 그룹에 속해 있는 모든 스레드들에 Interrupt가 전해지는 것을 확인할 수 있다.

ThreadGroup 메서드

ThreadGroup은 다음과 같은 메서드를 제공한다.

메서드 설명
activeCount() 현재 그룹과 하위 그룹에 속해 있는 스레드의 개수를 리턴
activeGroupCount() 현재 그룹의 하위 그룹의 개수를 리턴
checkAccess() 현재 스레드가 스레드 그룹을 변경할 권한이 있는지 체크
(만약 없다면 SecurityException 발생)
destroy() 현재 그룹과 하위 그룹들을 삭제
단 포함된 스레드들은 모두 종료상태여야 함
isDestroyed() 현재 그룹이 삭제 상태인지 확인
getMaxPriority() 현재 그룹에 속해 있는 스레드가 가질 수 있는 최대 우선순위를 리턴
setMaxPriority(int pri) 현재 그룹에 속해 있는 스레드가 가질 수 있는 최대 우선순위를 설정
getName() 현재 그룹의 이름 리턴
getParent() 혀재 그룹의 부모 그룹을 리턴
parentOf(ThreadGroup g) 입력 받은 그룹이 부모 그룹인지 확인
isDaemon() 현재 그룹이 데몬 그룹인지 여부를 리턴
setDaemon(boolean daemon) 데몬 그룹으로 설정 혹은 해제
list() 현재 그룹에 포함된 스레드와 하위 그룹에 대한 정보를 출력
interrupt() 현재 그룹에 포함된 모든 스레드들을 interrupt

 

댓글