스레드 그룹은 관련된 스레드를 묶어서 관리할 목적으로 이용된다.
JVM이 실행 되면 system 스레드 그룹을 만들고,
JVM 운영에 필요한 스레드들을 생성해서 system 스레드 그룹에 포함시킨다
그리고 system의 하위 스레드 그룹으로 main을 만들고 메인 스레드를 main 스레드 그룹에 포함시킨다.
스레드는 반드시 하나의 스레드 그룹에 포함되는데,
명시적으로 스레드 그룹에 포함시키지 않으면 기본적으로 자신을 생성한 스레드와 같은 스레드 그룹에 속하게 된다.
우리가 생성하는 작업 스레드는 대부분 main 스레드가 생성하므로 기본적으로 main 스레드 그룹에 속하게 된다.
스레드 그룹 이름 얻기
현재 스레드가 속한 스레드 그룹의 이름을 얻고 싶다면 다음과 같은 코드를 사용 할 수 있다.
ThreadGruop gruop = Thread.currentThread().getThreadGroup();
String groupName = gruop.getName();
Thread의 정적 메소드인 getAllStacksTraces() 를 이용하면 프로세스 내에서 실행하는 모든 스레드에 대한 정보를 얻을수 있다.
Map<Thread, StackTraceElement[]> map = Thread.getAllStackTrace();
getAllStackTraces() 메소드는 Map 타입의 객체를 리턴하는데,
키는 스레드 객체이고 값은 스레드의 상태 기록들을 갖고 있는 StackTraceElement[] 배열이다.
public class ThreadInfoExample{
public static void main(String[] args){
AutoSaveThread autiSaveThread = new AutoSaveThread();
autoSavaThread.setName("AutoSaveThread");
autoSaveThread.setDaemon(true);
autoSaveThread.start();
Map<Thread, StatckTraceElement[]> map = Thread.getAllStackTraces();
Set<Thread> threads = map.keySet();
for(Thread thread : threads){
System.out.print("Name: " + thread.getName() + ((thread.isDaemin()?"(데몬) : "(주)"")));
System.out.println("\t" + "소속그룹: " + thread.getThreadGruop().getName());
System.out.println();
}
}
실행 결과를 보면 가비지 컬렉션을 담담하는 Finalizer 스레드를 비롯한 일부 스레드들이 system 그룹에 속하고,
main() 메소드를 실행하는 main 스레드는 system 그룹의 하위 그룹인 main에 속하는 것을 볼수 있다
그리고 main 스레드가 실행시킨 AutoSaveThread는 main 스레드가 소속된 main 글부에 포함되어 있는 것을 볼수 있다.
스레드 그룹 생성
명시적으로 스레드 그룹을 만들고 싶다면 다음 생성자중 하나를 이용해서 ThreadGroup 객체를 만들면 된다.
ThreadGroup 이름만 주거나, 부모 ThreadGruop과 이름을 매개값으로 줄수 있다.
ThreadGroup tg = new ThreadGruop(String name);
ThreadGroup tg = new ThreadGruop(ThreadGruop parent, String name);
스레드 그룹 생성 시 부모(parent) 스레드 그룹을 지정하지 않으면 현재 스레드가 속한 그룹의 하위 그룹으로 생성된다.
예를 들어 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);
Runnalbe 타입의 target 은 Runnalbe 구현 객체를 말하며, String 타입의 name 은 스레드의 이름이다.
그리고 long 타입의 stackSize는 JVM이 이 스레드에 할당할 스택이 크기 이다
스레드 그룹의 일괄 interrupt()
스레드를 스레드 그룹에 포함시키면 어떤 이점이 있을까?
스레드 그룹에서 제공하는 interrupt()메소드를 이용하면 글부 내에 포함된 모든 스레드들 일괄 interrupt 할 수 있다.
예를 들어 10개의 스레드들을 모두 종료시키기 위해 각 스레드에서 interrupt() 메소드를 10번 호출 할 수도 있지만,
이 스레드 들이 같은 스레드 그룹에서 소속되어 있을 경우,
스레드 그룹이 interrupt() 메소드를 한번만 호출해주면 된다.
이것이 가능한 이유는 스레드 그룹이 interrupt() 메소드는
포함된 모든 스레드의 interrupt() 메소드를 내부적으로 호출해주기 때문이다 .
스레드 그룹의 interrupt() 메소드는 소속된 스레드의 interrupt() 메소드를 호출만 할 뿐 개별 스레드에서
발생하는 InterruptedException에 대한 예외 처리를 하지 않는다.
따라서 안전한 종료를 위해서는 개별 스레드가 예외 처리를 해야 한다.
스레드 그룹에는 interrupt() 메소드 이외에도 suspent(), resume(), strop 메소드들이 있는데
모두 Deprecated 되었다. stop() 메소드를 호출하면 그룹에 포함된 모든 스레드들의 stop() 메소드가 일괄 호출되어 모든 스레드들을 쉽게 종료 할 수 있으나, 스레드의 안전성 문제 때문에 가급적 사용하지 말아야 한다.
대신 interrupt() 메소드로 스레드들을 안전하게 종료하도록 유도하는 것이 좋다.
다음은 ThreadGroup이 가지고 있는 주요 메소드들이다.
메소드 | 설명 | |
int | activeCount() | 현재 그룹 및 하위 그룹에서 활동중인 모든 스레드의 수를 리턴한다 |
int | activeGroupCount() | 현재 그룹에서 활동 중인 모든 하위 그룹의 수를 리턴한다. |
void | checkAccess() | 현재 스레드가 스레드 그룹을 변경할 권한이 있는지 체크한다 만약 권한이 없으면 SercurityExcetption 을 발생 시킨다 |
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() 메소드를 호출해서 스래드 그룹에 포함된 모든 스레드들을 종료 시킨다
public class WorkThread extends Thread {
public WorkThread(ThreadGroup threadGroup , String threadName){
super(threadGruop, threadName);
}
@Override
public void run(){
while(true){
try{
Thread.sleep(1000);
} catch (InterruptedException e){
System.out.println(getname() + " interrupted");
break;
}
}
System.out.println(getName() + " 종료됨");
}
}
public class ThreadGroupExample{
public static void main(String[] args){
ThreadGroup myGruop = new ThreadGroup("myGroup");
WorkThread workThreadA = new WorkThread(myGruop, "workThreadA");
WorkThread workThreadB = new WorkThread(myGruop, "workThreadB");
workThreadA.start();
workThreadB.start();
System.out.println("[main 스레드 그룹의 list() 메소드 출력 내용]");
ThreadGroup mainGruop = Thread.currentThread().getThreadGroup();
mainGroup.list();
System.out.println();
try{Thread.sleep(3000;)} catch (InterruptedException);
System.out.println([myGroup 스레드 그룹의 interrupt() 메소드 호출]);
myGroup.interrupt();
}
}
실행 결과를 보면 list() 메소드는 현재 스레드 그룹의 이름과 최대 우선순위를 헤더로 출력하고,
그 아래에 현재 스레드 그룹에 포함된 스레드와 하위 스레드 그룹의 내용을 보여준다.
스레드는 [스레드이름, 우선순위, 소속그룹명] 으로 출력되는 것을 볼 수 있다.
스레드 그룹의 interrupt() 메소드를 호출하면 myGroup()에 포함된 두 스레드에서 InerruptedException이 발생되어 스레드가 종료되는 것을 알 수 있다.
'Back-end > 이것이 자바다[신용권 한빛미디어]' 카테고리의 다른 글
[제네릭] (0) | 2022.02.12 |
---|---|
스레드 풀 (0) | 2021.12.12 |
스레드 상태 제어 (0) | 2021.12.05 |
스레드 상태 (0) | 2021.12.04 |
동기화 메소드와 동기화 블록 (0) | 2021.12.04 |