티스토리 뷰
주의 사항!
- 이 글은 제가 직접 공부하는 중에 작성되고 있습니다.
- 따라서 제가 이해하는 그대로의 내용이 포함됩니다.
- 따라서 이 글은 사실과는 다른 내용이 포함될 수 있습니다.
스레드 그룹은 관련된 스레드를 묶어서 관리할 목적으로 이용됩니다. JVM이 실행되면 system 스레드 그룹을 만들고, JVM운영에 필요한 스레드들을 생성해서 system 스레드 그룹에 포함시킵니다. 그리고 system의 하위 스레드 그룹으로 main을 만들고 메인 스레드를 main 스레드 그룹에 포함시킵니다. 스레드는 반드시 하나의 스레드 그룹에 포함되는데, 명시적으로 스레드 그룹에 포함시키지 않으면 기본적으로 자신을 생성한 스레드와 같은 스레드 그룹에 속하게 됩니다. 우리가 생성하는 작업 스레드는 대부분 main 스레드가 생성하므로 기본적으로 main 스레드 그룹에 속하게 됩니다.
스레드 그룹 이름 얻기
현재 스레드가 속한 스레드 그룹의 이름을 얻고 싶다면 다음과 같은 코드를 사용할 수 있습니다.
ThreadGroup group = Thread.currentThread().getThreadGroup();
String groupName = group.getName();
Thread의 정적 메서드인 getAllStackTraces()를 이용하면 프로세스 내에서 실행하는 모든 스레드에 대한 정보를 얻을 수 있습니다.
Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
getAllStackTraces() 메서드는 Map 타입의 객체를 리턴하는데, 키는 스레드 객체이고 값은 스레드의 상태 기록들을 갖고 있는 StackTraceElement [] 배열입니다. Map 타입에 대한 자세한 내용은 나중에 배우게 됩니다.
다음 예제는 현재 실행하고 있는 스레드의 이름과 데몬 여부 그리고 속한 스레드 그룹의 이름이 무엇인지 출력합니다.
//Main.java
package Example;
import java.util.Map;
import java.util.Set;
public class Main {
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();
}
}
}
/*
실행결과
Name : AutoSaveThread(데몬)
소속그룹 : main
Name : Reference Handler(데몬)
소속그룹 : system
Name : Signal Dispatcher(데몬)
소속그룹 : system
Name : main(주)
소속그룹 : main
Name : Common-Cleaner(데몬)
소속그룹 : InnocuousThreadGroup
Name : Finalizer(데몬)
소속그룹 : system
Name : Notification Thread(데몬)
소속그룹 : system
Name : Attach Listener(데몬)
소속그룹 : system
*/
스레드 그룹 생성
명시적으로 스레드 그룹을 만들고 싶다면 다음 생성자 중 하나를 이용해서 ThreadGroup 객체를 만들면 됩니다. ThreadGroup 이름만 주거나, 부모 ThreadGroup과 이름을 매개 값으로 줄 수 있습니다.
ThreadGroup tg = new ThreadGroup(String name);
ThreadGroup tg = new ThreadGroup(ThreadGroup parent, String name);
스레드 그룹 생성 시 부모 스레드 그룹을 지정하지 않으면 현재 스레드가 속한 그룹의 하위 그룹으로 생성됩니다. 예를 들어 main 스레드가 ThreadGroup(String name)을 이용해서 새로운 스레드 그룹을 생성하면, main 스레드 그룹의 하위 스레드 그룹이 됩니다.
새로운 스레드 그룹을 생성한 후, 이 그룹에 스레드를 포함시키려면 Thread 객체를 생성할 때 생성자 매개 값으로 스레드 그룹을 지정하면 됩니다. 스레드 그룹을 매개 값으로 갖는 Thread 생성자는 다음 네 가지가 있습니다.
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);
Runnable 타입의 target은 Runnable 구현 객체를 말하며, String 타입의 name은 스레드의 이름입니다. 그리고 long 타입의 stackSize는 JVM이 이 스레드에 할당할 stack 크기입니다.
스레드 그룹의 일괄 interrupt()
스레드 그룹에서 제공하는 interrupt() 메서드를 이용하면 그룹 내에 포함된 모든 스레드들을 일괄 interrupt 할 수 있습니다. 예를 들어 10개의 스레드들을 모두 종료시키기 위해 각 스레드에서 interrupt() 메서드를 10번 호출할 수도 있지만, 이 스레드들이 같은 스레드 그룹에 소속되어 있을 경우, 스레드 그룹의 interrupt() 메서드를 한 번만 호출해주면 됩니다. 이것이 가능한 이유는 스레드 그룹의 interrupt() 메서드는 포함된 모든 스레드의 interrupt() 메서드를 내부적으로 호출해주기 때문입니다.
스레드 그룹의 interrupt() 메서드는 소속된 스레드의 interrupt() 메서드를 호출만 할 뿐 스레드에서 발생하는 InterruptedException 예외에 대한 예외 처리를 하지 않습니다. 따라서 안전한 종료를 위해서는 개별 스레드가 예외 처리를 해야 합니다.
스레드 그룹에는 interrupt() 메서드 이외에도 suspend(), resume(), stop() 메서드들이 있는데, 모두 Deprecated 되었습니다. stop() 메서드를 호출하면 그룹에 포함된 모든 스레드들의 stop() 메서드가 일괄 호출되어 모든 스레드들을 쉽게 종료할 수 있지만, 스레드의 안전성 문제 때문에 가급적 사용하지 말아야 합니다. 대신 interrupt() 메서드로 스레드들을 안전하게 종료하도록 유도하는 것이 좋습니다. 다음은 ThreadGroup이 가지고 있는 주요 메서드들입니다.
메서드 | 설명 | |
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합니다. |
다음 예제는 스레드 그룹을 생성하고, 정보를 출력해 봅니다. 그리고 3초 후 스레드 그룹의 interrupt() 메서드를 호출해서 스레드 그룹에 포함된 모든 스레드들을 종료시킵니다.
//WorkThread.java
package Example;
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) {
System.out.println(getName() + " interrupted");
break;
}
}
System.out.println(getName() + " 종료됨");
}
}
//Main.java
package Example;
public class Main {
public static void main(String[] args) {
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
workThreadA interrupted
workThreadB 종료됨
workThreadA 종료됨
*/
'공부 일지 > JAVA 공부 일지' 카테고리의 다른 글
자바, 스레드풀 (0) | 2021.04.28 |
---|---|
자바, 데몬 스레드 (2) | 2021.04.27 |
자바, 스레드 상태 제어 (0) | 2021.04.26 |
자바, 스레드 상태 (0) | 2021.04.26 |
자바, 동기화 메서드와 동기화 블록 (0) | 2021.04.26 |