메뉴 건너뛰기

2016.09.13 18:50

쓰레드의 실행제어

조회 수 3264 추천 수 0 댓글 0
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄

쓰레드의 실행제어


쓰레드 프로그래밍이 어렵다고 하는 이유는 동기화와 스케줄링 때문이다. 앞서 우선순위를 통해 쓰레드간의 스케줄링을 하는 방법을 제시하였지만, 사실 이것만으로는 부족하다.

효율적인 멀티쓰레드 프로그램을 만들기 위해서는 보다 정교한 스케줄링을 통해 프로세스에게 주어진 자원과 시간을 여러 쓰레드가 낭비없이 잘 사용하도록 프로그래밍 해야 한다.


1. 쓰레드의 스케줄링과 관련된 메서드


2. 쓰레드의 상태


3. 쓰레드의 생성부터 소멸까지의 모든 과정

1 쓰레드를 생성하고 start()를 호출하면 바로 실행되는 것이 아니라 실행대기열에 저장되어 자신의 차례가 될 때까지 기다려야 한다. 실행대기열은 큐(queue)와 같은 구조로 먼저 실행대기열에 들어온 쓰레드가 먼저 실행된다.


2 실행대기상태에 있다가 자신의 차례가 되면 실행상태가 된다.


3 주어진 실행시간이 다되거나 yeild()를 만나면 다시 실행대기상태가 되고 다음 차례의 쓰레드가 실행상태가 된다.


4 실행 중에 suspecd(), sleep(), wait(), join(), I/O block에 의해 일시정지상태가 될 수 있다. I/O block은 입출력작업에서 발생하는 지연상태를 말한다. 사용자의 입력을 기다리는 경우를 예로 들 수 있는데, 이런 경    우 일시정지 상태에 있다가 사용자가 입력을 마치면 다시 실행대기상태가 된다.


5 지정된 일시정지시간이 다되거나(time-out), notify(), resume(), interrupt()가 호출되면 일시정지 상태를 벗어나 다시 실행대기열에 저장되어 자신의 차례를 기다리게 된다.


6 실행을 모두 마치거나 stop()이 호출되면 쓰레드는 소멸된다.

cf.) 1부터 6까지 번호를 붙였으나 번호의 순서대로 쓰레드가 수행되는 것은 아니다.


4. 예제

class ThreadEx13 {

static long startTime = 0;


public static void main(String args[]) {


A th1 = new A();

B th2 = new B();


th1.start();

th2.start();

startTime = System.currentTimeMillis();


try {

th1.join(); // th1의 작업이 끝날 때까지 기다린다.

th2.join(); // th2의 작업이 끝날 때까지 기다린다.

} catch(InterruptedException e) {}


System.out.print("소요시간:" + (System.currentTimeMillis() - ThreadEx13.startTime));

} // main

}


class A extends Thread {

public void run() {

for(int i=0; i < 300; i++) {

System.out.print("-");

}

} // run()

}


class A extends Thread {

public void run() {

for(int i=0; i < 300; i++) {

System.out.print("|");

}

} // run()

}

실행결과)

------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||소요시간:13

join()을 사용하지 않으면 main쓰레드는 바로 종료되지만, join()을 사용해서 쓰레드 th1과 th2의 작업을 마칠 때 까지 main 쓰레드가 기다리도록 했다.

만약 join(long millis)이나 join(long millis, int nanos)를 사용하면 지정된 시간만큼만 main 쓰레드가 기다리도록 할 수 있다. 이처럼 한 쓰레드의 작업의 중간에 다른 쓰레드의 작업이 필요할 때 join()을 사용한다.


class ThreadEx14 {

public static void main(String args[]) {

A th1 = new A();

  B th2 = new B();


th1.start();

try {

th1.join(); 

  // join(): 두 개 이상의 쓰레드가 동작할 시 하나의 쓰레드에 대해서 지속을 거는 것. 두 개의 쓰레드가 진행하고 있는데 한 쓰레드에 대해서 join 걸면                                           // 그 쓰레드가 끝날때 까지 기다려준다.

  // cf.) sleep()은 전체 쓰레드에 대해 지연을 건다. 하지만 join()은 특정 쓰레드에 대해 지연을 건다.

} catch(InterruptedException e) {}

th2.start();

}

}


class A extends Thread {

public void run() {

for(int i=0; i < 300; i++) {

System.out.print("-");

}

}

}


class B extends Thread {

public void run() {

for(int i=0; i < 300; i++) {

System.out.print("|");

}

}

}

실행결과)

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

join()이 사용된 부분을 주석처리하고 실행한 결과이다.

두 쓰레드가 번갈아 가며 실행되지 않고, 순차적으로 실행해야할 때 join()을 사용해서 해결하는 방법을 보여주는 예제이다.


cf.) 바로 위 예제에 join() 부분을 주석처리한 경우

/*try {th1.join();} catch(InterruptedException e) {}*/

실행결과)

--|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||------------------------------------------------------------------------------------|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

두 쓰레드가 순차적으로 실행되지 않고 번갈아 가며 실행되는 모습이다.


 class ThreadEx15{

public static void main(String args[]) {

A th1 = new A();

B th2 = new B();


th1.start();

th2.start();


try {

th1.sleep(5000);

  // sleep(): 작업 흐름 대기시간 설정한다. 5초동안 대기시간 갖은 후에 다음 문자의 실행흐름을 이어 나간다.

} catch(InterruptedException e) {}


System.out.print("<<main 종료>>");

} // main

}


class A extends Thread {

public void run() {

for(int i=0; i < 300; i++) {

System.out.print("-");

}

System.out.print("<<th1 종료>>");

} // run()

}


class B extends Thread {

public void run() {

for(int i=0; i < 300; i++) {

System.out.print("|");

}

System.out.print("<<th2 종료>>");

} // run()

}

실행결과)

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------||||||||||||||||||||||||||||-------------------------------------<<th1 종료>>||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||<<th2 종료>><<main 종료>>

결과를 보면 쓰레드 th1의 작업이 가장 먼저 종료되었고, 그 다음이 th2, main의 순인 것을 알 수 있다.

th1과 th2에 대해 start()를 호출하자마자 th1.sleep(5000)을 호출하여 쓰레드 th1이 5초동안 작업을 멈추고 일시정지상태에 있도록 하였으니까 쓰레드 th1이 가장 늦게 종료되어야 하는데 결과에서는 제일 먼저 종료되었다. 그 이유는 sleep()이 항상 현재 실행 중인 쓰레드에 대해 작동하기 때문에 th1.sleep(5000)과 같이 호출하였어도 실제로 영향을 받는 것은 main 메서드를 실행하는 main 쓰레드이다.

그래서 sleep()은 static으로 선언되었으며 참조변수를 이용해서 호출하기 보다는 Thread.sleep(5000);과 같이 해야 한다. yield() 또한 이와 같은 이유에서 static으로 선언되어 있어서 항상 현재

실행 중인 쓰레드에 대해 동작하며 Thread.yield()와 같이 호출해야 한다.


cf.) 위의 예제 중 th1.sleep(5000) 대신 Thread.sleep(5000)

try {Thread.sleep(5000);} catch(InterruptedException e) {}

실행결과)

||||||||||||||||||||||--------------------------|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||----------------|||||||||||||||||||||||--------------------------------------------------------------------------------------------------------------------------|||||||||||||||||||||||||||||||||||||||||||||||||||||||---------------------|||||||||||||||-------------------------------------------------------------------------------<<th2 종료>>------------------------------------<<th1 종료>><<main 종료>>


 class ThreadEx19 {

public static void main(String args[]) {

MyThreadEx19 th1 = new MyThreadEx19("*");

MyThreadEx19 th2 = new MyThreadEx19("**");

MyThreadEx19 th3 = new MyThreadEx19("***");

th1.start();

th2.start();

th3.start();


try {

Thread.sleep(2000);

th1.suspend();

Thread.sleep(2000);

th2.suspend();

Thread.sleep(3000);

th1.resume();

Thread.sleep(3000);

th1.stop();

th2.stop();

Thread.sleep(2000);

th3.stop();

} catch (InterruptedException e) {}

}

}


class MyThreadEx19 implements Runnable {

boolean suspended = false;

boolean stopped = false;


Thread th;


MyThreadEx19(String name) {

th = new Thread(this, name); // Thread(Runnable r, String name)

}


public void run() {

String name = Thread.currentThread().getName();


while(!stopped) {

if(!suspended) {

System.out.println(name);

try {

Thread.sleep(1000);

} catch(InterruptedException e) {

System.out.println(name + " - interrupted");

}

} else {

Thread.yield();

}

}

System.out.println(name + " - stopped");

}


public void suspend() {

suspended = true;

th.interrupt();

// interrupt(): 특정 객체를 멈추고자 할때 사용, 쓰레드의 권한 중지

System.out.println("interrupt() in suspend()");

}


public void resume() {

suspended = false;

}


public void stop() {

stopped = true;

th.interrupt();

System.out.println("interrupt() in stop()");

}


public void start() {

th.start();

}

}

실행결과)

*

**

***

*

**

***

*

interrupt() in suspend()

* - interrupted

***

**

**

***

** - interrupted

interrupt() in suspend()

***

***

***

*

***

*

***

*

***

*

interrupt() in stop()

interrupt() in stop()

* - interrupted

* - stopped

** - stopped

***

***

interrupt() in stop()

*** - interrupted

*** - stopped

- yield(): yield()를 호출해서 남은 실행시간을 while문에서 낭비하지 않고, 다른 쓰레드에게 양보하게 된다.

- interrupt(): InterruptedException을 발생시켜서 sleep(), join(), wait()에 의해 일시정지 상태인 쓰레드를 실행대기상태로 만든다. interrupt()가 호출되었을 때, sleep(), join(), wait()에 의한 일시정지상태가 아니라면 아무런 일도 일어나지 않는다. 만일 stop()이 호출되었을 때 Thread.sleep(1000)에 의해 쓰레드가 일시정지상태에 머물러 있는 상황이라면 쓰레드가 정지될 때까지 최대 약 1초의 시간지연이 생길 것이다. 그래서 잠자고 있는 쓰레드를 깨울 interrupt()가 필요하다. 같은 상황에서 interrupt()가 호출되면, Thread.sleep(1000)에서 InterruptedException이 발생하고 즉시 일시정지상태에서 벗어나 catch 블럭 내의 문장이 실행된다.

이처럼 interrupt()를 사용하면 쓰레드의 응답성을 높일 수 있다.



List of Articles
번호 제목 날짜 조회 수
111 A java Runtime Environment(JRE) or Java Development Kit(JDK) must be ~~~~ 하면서 이클립스가 실행안될때. file 2019.03.05 788
110 변환 (문자, 숫자, KSC5601.....) 2019.01.16 1281
109 기본적인 스크립트 보안 2019.01.16 6041
108 JAVA/JSP SQL Injection 해킹 방지를 위한 코딩 2019.01.10 846
107 Java : JSOUP 를 이용, html에서 소스, 링크경로 추출후 절대 경로로 바꾸기 2019.01.08 1166
106 Apache Commons HttpClient 3.x 로 Http 서버에 파일 전송하기 file 2019.01.08 1136
105 JSON 문자열을 Map 으로 변환하기(Jackson 사용) 2019.01.08 1010
104 시간관련 클래스 file 2018.09.21 1186
103 직렬화 / 역직렬화 file 2018.09.21 951
102 예외처리 / 예외발생 file 2018.09.21 934
101 JAVA 현재 시간 구하기 file 2018.07.09 1266
100 JAVA 이클립스 인코딩 변경하기 file 2018.07.09 1587
99 JAVA JDK 제거하기 file 2018.07.09 1409
98 JAVA CentOS JDK 설치 및 환경변수 설정 file 2018.07.09 1821
97 JAVA 정규표현식을 이용한 패턴매칭(HTML 제거) 2018.07.09 1164
96 JAVA 두개의 문서 파일 비교하기 2018.07.09 2388
95 JAVA 인코딩을 변경하여 파일 출력하기 (EUC_KR) 2018.07.09 1105
94 JAVA TreeMap 인덱스 값 가져오기 및 Collections.sort 사용하기 2018.07.09 1483
93 JAVA public, private, protected 정리 2018.07.09 1001
92 JAVA HashMap의 Key값 출력하기 2018.07.09 1069
Board Pagination Prev 1 2 3 4 5 6 7 8 Next
/ 8

하단 정보를 입력할 수 있습니다

© k2s0o1d4e0s2i1g5n. All Rights Reserved