본문 바로가기
Language/Java

[Java] 쓰레드 3 - 쓰레드 우선순위와 쓰레드 그룹

by 계범 2022. 3. 14.

우선순위 지정

쓰레드는 우선순위를 통해 각 쓰레드 별 작업시간을 다르게 가지게 할 수 있다.

 

void setPriority(int newPriority) : 쓰레드의 우선순위를 지정한 값으로 변경한다.
void getPriority() : 쓰레드의 우선순위를 반환한다.

public static final int MAX_PRIORITY = 10 // 최대우선순위
public static final int MIN_PRIORITY = 1 // 최소우선순위
public static final int NORM_PRIORITY = 5 // 보통우선순위

 

쓰레드가 가질 수 있는 우선순위 범위는 1~10이며 숫자가 높을수록 우선순위가 높다.

쓰레드의 우선순위는 쓰레드를 생성한 쓰레드로부터 상속받는다.

main메서드를 수행하는 쓰레드의 우선순위는 5이다.

쓰레드의 우선순위 변경은 쓰레드를 실행하기전에만 가능하다.

 

class ThreadEx8 {
	public static void main(String args[]) {
		ThreadEx8_1 th1 = new ThreadEx8_1();
		ThreadEx8_2 th2 = new ThreadEx8_2();

		th2.setPriority(7);

		System.out.println("Priority of th1(-) : " + th1.getPriority() );
		System.out.println("Priority of th2(|) : " + th2.getPriority() );
		th1.start();
		th2.start();
	}
}

class ThreadEx8_1 extends Thread {
	public void run() {
		for(int i=0; i < 300; i++) {
			System.out.print("-");
			for(int x=0; x < 10000000; x++);
		}
	}
}

class ThreadEx8_2 extends Thread {
	public void run() {
		for(int i=0; i < 300; i++) {
			System.out.print("|");
			for(int x=0; x < 10000000; x++);
		}
	}
}

 

싱글코어에선 우선순위에 따른 차이가 크지만(A가 더 빨리 작업됨),

멀티코어에선 위의 코드가 수행하는데 큰 효과가 없었다.

 

 멀티코어라해도 OS마다 다른 방식으로 스케쥴링을 진행하기 때문에, 어떤 OS에서 실행하느냐에따라 다른 결과를 얻을 수 있다. 특정 OS의 스케쥴링 정책과 JVM의 구현을 직접 확인해봐야 우선순위에 따른 구현을 확인해 볼 수 있을 것이다.

 

멀티코어환경에선 우선순위 부여 대신 작업에 우선순위를 두어 PriorityQueue에 저장해 놓고, 우선순위가 높은 작업이 먼저 처리되도록 하는 것이 나을 수도 있다.

 

쓰레드 그룹(thread group)

쓰레드 그룹은 서로 관련된 쓰레드를 그룹으로 다루기 위한 것이다.

 

보안상의 이유로 도입된 개념으로, 자신이 속한 쓰레드 그룹이나 하위 쓰레드 그룹은 변경할 수 있지만 다른 쓰레드 그룹의 쓰레드를 변경할 수는 없다.

 

ThreadGroup을 사용해서 생성할 수 있으며, 주요 생성자와 메서드는 다음과 같다.

 

생성자 / 메서드 설 명
ThreadGroup(String name) 지정된 이름의 새로운 쓰레드 그룹을 생성
ThreadGroup(ThreadGroup parent, String name) 지정된 쓰레드 그룹에 포함되는 새로운 쓰레드 그룹을 생성
int activeCount() 쓰레드 그룹에 포함된 활성상태에 있는 쓰레드의 수 반환
int activeGroupCount() 쓰레드 그룹에 포함된 활성상태에 있는 쓰레드 그룹의 수 반환
void checkAccess() 현재 실행중인 쓰레드가 쓰레드 그룹을 변경할 권한이 있는지 체크.
만일 권한이 없다면 SecurityException을 발생시킨다.
void destroy() 쓰레드 그룹과 하위 쓰레드 그룹까지 모두 삭제한다.
단, 쓰레드 그룹이나 하위 쓰레드 그룹이 비어있어야 한다.
int enumerate(Thread[] list)
int enumerate(Thread[] list, boolean recurse)
int enumerate(ThreadGroup[] list)
int enumerate(ThreadGroup[] list, boolean recurse)
쓰레드 그룹에 속한 쓰레드 또는 하위 쓰레드 그룹의 목록을 지정된 배열에 담고 그 개수를 반환.
두 번째 매개변수인 recurse의 값을 true로 하면 쓰레드 그룹에 속한 하위 쓰레드 그룹에 쓰레드 또는 쓰레드 그룹까지 배열에 담는다.
int getMaxPriority() 쓰레드 그룹의 최대우선순위를 반환
String getName() 쓰레드그룹의 이름을 반환
ThreadGroup getParent() 쓰레드 그룹의 상위 쓰레드그룹을 반환
void interrupt() 쓰레드 그룹에 속한 모든 쓰레드를 interrupt
boolean isDaemon() 쓰레드 그룹이 데몬 쓰레드그룹인지 확인
boolean isDestroyed() 쓰레드 그룹이 삭제되었는지 확인
void list() 쓰레드 그룹에 속한 쓰레드와 하위 쓰레드그룹에 대한 정보를 출력
boolean parentOf(ThreadGroup g) 지정된 쓰레드 그룹의 상위 쓰레드 그룹인지 확인
void setDaemon(boolean daemon) 쓰레드 그룹을 데몬 쓰레드그룹으로 설정/해제
void setMaxPriority(int pri) 쓰레드 그룹의 최대우선순위를 지정

 

쓰레드를 쓰레드 그룹에 포함시키려면 Thread생성자를 이용한다.

Thread(ThreadGorup group, String name)
Thread(ThreadGorup group, Ruunable target)
Thread(ThreadGorup group, Ruunable target, String name)
Thread(ThreadGorup group, Ruunable target, String name, long stackSize)

 

모든 쓰레드는 반드시 쓰레드 그룹에 포함되어 있어야한다.

기본적으로 자신을 생성한 쓰레드와 같은 쓰레드 그룹에 속하게 된다.

 

JVM은 main과 system이라는 쓰레드 그룹을 만들고 JVM운영에 필요한 쓰레드들을 생성해서 이 쓰레드 그룹에 포함시킨다.

가비지컬렉션은 system, main메서드는 main 쓰레드 그룹에 속함.

 

우리가 생성하는 모든 쓰레드 그룹은 main쓰레드 그룹의 하위 쓰레드 그룹이 되고, 지정하지 않고 생성한 쓰레드는 자동적으로 main쓰레드 그룹에 속하게 된다.

 

Thread의 쓰레드 그룹과 관련된 메서드는 다음과 같다.

ThreadGroup getThreadGroup() : 쓰레드 자신이 속한 쓰레드 그룹을 반환한다.
void uncaughtException(Thread t, Throwable e) : 쓰레드 그룹의 쓰레드가 처리되지 않은 예외에 의해 실행이 종료되었을 때, JVM에 의해 이 메서드가 자동적으로 호출된다.

 

 class ThreadEx9 {
	public static void main(String args[]) throws Exception {
		ThreadGroup main = Thread.currentThread().getThreadGroup();
		ThreadGroup grp1 = new ThreadGroup("Group1");
		ThreadGroup grp2 = new ThreadGroup("Group2");

		// ThreadGroup(ThreadGroup parent, String name) 
		ThreadGroup subGrp1 = new ThreadGroup(grp1,"SubGroup1"); 

		grp1.setMaxPriority(3);	// 쓰레드 그룹 grp1의 최대우선순위를 3으로 변경.
		
		Runnable r = new Runnable() {
			public void run() {
				try { 
					Thread.sleep(1000); // 쓰레드를 1초간 멈추게 한다.
				} catch(InterruptedException e) {}
			}	
		};

         // Thread(ThreadGroup tg, Runnable r, String name)
		Thread th1 = new Thread(grp1,     r, "th1"); 
		Thread th2 = new Thread(subGrp1,  r, "th2");
		Thread th3 = new Thread(grp2,     r, "th3");   

		th1.start();
		th2.start();
		th3.start();

		System.out.println(">>List of ThreadGroup : "+ main.getName() 
                           +", Active ThreadGroup: " + main.activeGroupCount()
                           +", Active Thread: "      + main.activeCount());
		main.list();
	}
}
// 결과
List of ThreadGroup : main, Active ThreadGroup: 3, Active Thread: 4
java.lang.ThreadGroup[name=main,maxpri=10]
    Thread[main,5,main]
    java.lang.ThreadGroup[name=Group1,maxpri=3]
        Thread[th1,3,Group1]
        java.lang.ThreadGroup[name=SubGroup1,maxpri=3]
            Thread[th2,3,SubGroup1]
    java.lang.ThreadGroup[name=Group2,maxpri=10]
        Thread[th3,5,Group2]

쓰레드 그룹과 쓰레드를 생성하고 main.list()를 호출해서 main쓰레드 그룹의 정보를 출력하는 예제.

 

정보 출력전에 쓰레드가 종료될 수 있으므로, sleep()을 호출해서 1초간 멈춤.

 

쓰레드 그룹에 포함된 하위 쓰레드 그룹이나 쓰레드는 들여쓰기를 이요해서 구별되어 있음.

 

새로 생성한 모든 쓰레드 그룹은 main쓰레드 그룹의 하위 쓰레드 그룹으로 포함.

쓰레드 그룹의 우선순위 설정(setMaxPriority)은 쓰레드가 그룹에 추가되기 이전에 설정되어야함.

후에 여기에 속한 쓰레드 그룹과 쓰레드가 영향 받음.

 

참조변수 없이 쓰레드를 생성해서 실행시켰지만, 가비지 컬렉션에 의해 제거되진 않음.

쓰레드의 참조가 쓰레드그룹에 저장되어 있기 때문이다.

 

참조

'Java의 정석' 책

댓글