우선 순위(Priority)가 높은 스레드는 우선 순위가 낮은 스레드에 비해 실행시간을 많이 할당받는다. 다른 스레드에 비하여 어떤 스레드가 많은 실행시간을 필요로 한다면 우선 순위를 높게 두면 된다. 스레드의 우선 순위를 지정하는 메소드로 setPriority가 있다.
public final void setPriority(int newPriority) |
newPrority값으로 1~10까지의 정수가 사용하는데 클수록 우선 순위가 높다.
Thread 클래스에 우선 순위와 관련된 상수가 다음과 같이 선언되어 있다.
public final static int MIN_PRIORITY = 1; // 가장 낮은 우선 순위 public final static int NORM_PRIORITY = 5; // 기본 우선 순위 public final static int MAX_PRIORITY = 10; // 가장 높은 우선 순위 |
A 스레드가 1의 우선 순위를 가졌고, B 스레드가 10의 우선 순위를 가졌다면 B스레드가 할당받은 실행 시간에 비해 A 스레드는 지극히 적은 시간을 할당받는다.
다음 예제를 해 보자.
Thread6.java |
|
public class Thread6 extends Thread{
public void run(){
for(int i=1;i<=10;i++)
System.out.println(getName()+": "+i);
}
public static void main(String[] args){
Thread6 t1=new Thread6();
Thread6 t2=new Thread6();
t1.setPriority(Thread.MIN_PRIORITY); // 우선 순위를 가장 낮게
t2.setPriority(Thread.MAX_PRIORITY); // 우선 순위를 가장 높게
t1.start();
t2.start();
}
}
출력 결과 |
|
Thread-2: 1
Thread-2: 2
...
Thread-1: 9
Thread-1: 10
출력 결과에서 보이듯이 우선 순위가 높은 스레드 t2가 실행시간을 많이 할당받는다. t1이 먼저 start()하더라도 t2에 비해 우선 순위가 낮아서 t2가 끝나기 전까지 거의 실행시간을 할당받지 못했음을 알 수 있다.
우선 순위를 지정하지 않으면 기본 우선 순위(NORM_PRIORITY)를 가진다.
thread 재우기와 깨우기
스레드를 재운다는 말은 스레드를 잠시 대기 상태로 만든다는 것이다. sleep 메소드는 주어진 시간 동안 스레드를 대기 상태로 만든다. 주어진 시간이 지나면 대기 상태를 벗어나 하던 일을 계속 실행한다.
public static native void sleep(long millis) throws InterruptedException; |
millis(밀리초)까지 스레드를 재운다. |
public static void sleep(long millis, int nanos) throws InterruptedException |
millis(밀리초)+nanos(나노초)까지 스레드를 재운다. |
다음 예제를 실행시키면 약 1초에 한번씩 x1행이 실행된다.
Thread7.java |
|
public class Thread7 extends Thread{
public void run(){
for(int i=1;i<=10;i++){
System.out.println(getName()+": "+i); // x1
try{
sleep(1000); // 1초간 대기하라...!!
}catch(InterruptedException ie){}
}
}
public static void main(String[] args){
Thread7 t1=new Thread7();
t1.start();
}
}
다음 예제를 해보자.
Thread8.java |
|
// 개행 문자를 출력하고 약 1초 동안 대기하는 쓰레드
class NewLine extends Thread{
public void run(){
for(int i=1;i<=10;i++){
System.out.println(); // 개행 문자 출력
try{
sleep(1000); // 1초 동안 대기
}catch(InterruptedException ie){}
}
}
}
// 정수를 출력하고 약 0.1초 동안 대기하는 쓰레드
public class Thread8 extends Thread{
public void run(){
for(int i=1;i<=100;i++){
System.out.print(i); // print 메소드는 개행 문자를 출력하지 않는다.
try{
sleep(100); // 0.1초 동안 대기
}catch(InterruptedException ie){}
}
}
public static void main(String[] args){
Thread8 t=new Thread8();
NewLine n=new NewLine();
// 두 스레드를 동시에 실행시킨다.
t.start();
n.start();
}
}
출력 결과 |
|
1
2345678910
...
81828384858687888990
919293949596979899100
t스레드는 약 0.1초에 한번씩 정수를 출력하고 n스레드는 약 1초에 한번씩 개행 문자를 출력하는 것을 볼 수 있을 것이다.
혼자 해보기 |
Alone14_2.java |
"타자기 효과의 문자 찍기" 문자열의 처음 문자부터 1초당 한 글자씩 출력하는 프로그램을 만들어보자.
출력 결과 예시 |
|
처음: 타
1초 후: 타자
2초 후: 타자기
....
13초 후: 타자기 효과의 문자 찍기
interrupt 메소드를 이용하면 자고 있는 스레드를 깨울 수 있다. 깨운다는 것은 대기 상태에 있는 스레드를 실행 가능 상태로 만든다는 말이다. interrupt는 '방해하다'의 뜻이다. 말 그대로 자고 있는 스레드를 방해한다.
public void interrupt() // 자고 있는 스레드를 실행 가능 상태로 만든다. |
sleep 메소드나 join 메소드로 인해 대기 상태가 된 스레드의 interrupt 메소드를 호출하면 sleep 메소드나 join 메소드에서 InterruptedException이 발생한다.
다음 예제를 해보자.
Thread9.java |
|
public class Thread9 extends Thread{
public void run(){
System.out.println("잘테니 깨우지마.");
try{
sleep(1000000); // x1
}catch(InterruptedException ie){ // x2
System.out.println("헐~ 누가 깨웠어?"); // x3
}
}
public static void main(String[] args){
Thread9 t=new Thread9();
t.start(); // x4
t.interrupt(); // x5
}
}
출력 결과 |
|
잘테니 깨우지마.
헐~ 누가 깨웠어?
x4행에서 스레드를 start시키면 x1행에서 곧바로 많은 시간동안 스레드는 잠이 든다. 그러나 x5행에서 main 스레드가 t를 깨우게 되는데, 자다가 방해를 받은 t는 x1행의 sleep 메소드에서 InterruptedException를 던진다. 그래서 x3행이 실행된다.
thread 양보하기
yield 메소드를 이용하여 다른 스레드에게 실행을 양보할 수 있다.
public static native void yield(); |
다른 스레드에게 실행을 양보한다. |
yield 메소드를 사용해서 특정 스레드에게 실행을 양보할 수는 없다. 살아있는 다른 임의의 스레드가 실행될 것이다.
다음 예제를 해보자.
Thread10.java |
|
public class Thread10 extends Thread{
public void run(){
for(int i=1;i<=10;i++){
System.out.println(getName()+": "+i); // x1
yield(); // x2, 다른 스레드에게 실행을 양보한다.
try{
sleep(100);
}catch(InterruptedException ie){}
}
}
public static void main(String[] args){
Thread10 t1=new Thread10();
Thread10 t2=new Thread10();
t1.start();
t2.start();
}
}
출력 결과 |
|
Thread-2: 1
Thread-1: 1
...
Thread-1: 5
Thread-2: 5
...
Thread-1: 10
Thread-2: 10
x2행의 yield()가 있을 때와 없을 때의 차이를 비교해 보면 없을 때 보다 있을 때 두 스레드가 번갈아 가며 x1행을 실행할 확률이 높다는 것을 확인할 수 있다. t1과 t2는 서로 실행을 양보(남을 위하여 자신의 이익을 희생하는 것)하기 때문이다.
짚어두기 |
|
스레드로부터 양보의 미덕을 배우자. 요즘 지하철이나 버스를 타보면 노약자에게 자리를 양보하는 사람은 몇몇에 불과하다. 자리를 양보하더라도 억지로 하는 사람이 대부분이다. 필자는 노약자가 다른 노약자에게 양보하는 것을 많이 봐왔다. 많이 가져서 베푸는 양보보다는 부족할 때 베푸는 양보가 더 아름다운 것 같다. 자신이 좌석에 앉아야할 만큼 피곤할 때 양보를 해보자. 그만큼 얻는 것도 클 것이다. |
동기화(syncronization)
다중 스레드를 실행하면 여러 가지 유추할 수 있는 문제가 발생할 수 있다. 예를 들어, 어떤 두 스레드 A와 B가 같은 프린터를 사용하여 자신들의 데이터를 인쇄한다고 생각하자. 먼저 A가 주어진 시간 동안 인쇄하다가, 다른 스레드에게 실행 순서가 넘어가서 대기상태가 된다. 그러면 실행 권한을 얻은 B가 자신의 데이터를 인쇄하다가 실행 순서가 다른 스레드에게로 넘어간다. 계속해서 A스레드가 실행 권한을 얻어서 하던 작업을 연이어 할 것이다. 이와 같이 두 스레드가 공유하고 있는 프린터로 동시에 인쇄하면 A와 B의 실행이 모두 끝났을 때, 인쇄 결과는 불 보듯 뻔하다. 엉망진창.
반면에 A스레드가 자신의 데이터를 완전히 인쇄한 후에, B스레드가 인쇄를 시작하면 문제가 없을 같다. 프린터에 대한 사용 권한을 하나의 스레드만이 가질 수 있다고 생각하자. 먼저 A스레드가 프린터에 대한 사용 권한을 가지고 인쇄 작업을 시작할 것이다. 실행 권한이 B로 넘어가더라도 B는 프린터에 대한 사용 권한이 없어서 대기 상태가 된다. A스레드가 모든 인쇄 작업을 마치고 프린터에 대한 사용 권한을 포기하면 대기하고 있던 B스레드가 프린터에 대한 사용 권한을 얻어서 인쇄를 시작한다.
이와 같이 스레드들이 공유하고 있는 자원에 대한 권한을 얻은 스레드만이 어떤 작업을 수행할 수 있도록 하는 것을 동기화(syncronization)라고 한다.
다음 예제를 실행해 보자.
Synchronization1.java |
|
class PrintThread extends Thread{
char ch;
public PrintThread(char ch){ // 생성자, ch는 화면에 출력될 문자이다.
this.ch=ch;
}
void printCh10(){ // 화면에 해당 문자를 10회 출력한다.
for(int i=1;i<=10;i++)
System.out.print(ch); // 개행 문자를 출력하지 않음에 유의하자.
}
public void run(){
for(int i=1;i<=10;i++){
printCh10(); // 문자를 10회 출력하고
System.out.println(); // 개행 문자를 출력한다.
}
}
}
public class Synchronization1{
public static void main(String[] args){
PrintThread pt=new PrintThread('A');
pt.start();
}
}
출력 결과 |
|
AAAAAAAAAA
AAAAAAAAAA
...
AAAAAAAAAA
AAAAAAAAAA
어려운 예제가 아니므로 쉽게 이해할 수 있을 것이다. 하지만 다음 예제와 같이 두 개 이상의 스레드가 실행되면 어떻게 출력될까?
Synchronization2.java |
|
public class Synchronization2{
public static void main(String[] args){
PrintThread pt1=new PrintThread('A');
PrintThread pt2=new PrintThread('B');
pt1.start();
pt2.start();
}
}
출력 결과 |
|
AAAAAAAAAA
...
BABABABABABABABABABA
...
BABABABABABABABABABA
...
BBBBBBBBBB
매번 실행될 때마다 위 출력 결과와 다르게 출력될 수 있다. pt1과 pt2는 서로 경쟁하면서 화면에 출력하기 때문에 출력 결과가 뒤죽박죽인 것은 당연하다. 만약 3개 이상의 스레드가 한꺼번에 start된다면 더더욱 뒤죽박죽 된다.
이런 문제에 대한 해결 방안은 동기화에 있다. 동기화를 위한 synchronized block과 synchronized method에 대하여 알아보자.
'Document' 카테고리의 다른 글
AWT[1/2] (0) | 2008.07.25 |
---|---|
JSAPI란.. (0) | 2008.07.24 |
자바에서 예외처리 (0) | 2008.07.17 |
Java Collection 중 자주 사용하는 것 (0) | 2008.07.15 |
Collection Framework (0) | 2008.07.15 |