사용자 삽입 이미지
사용자 삽입 이미지
 
사용자 삽입 이미지
Program / Process / Thread
사용자 삽입 이미지
 그럼 여기서 먼저 혼동이 될 수 있는 용어를 먼저 정리해 보겠습니다.
사용자 삽입 이미지
 
Program어떤 일을 컴퓨터로 수행하기 위한 코드 그 자체
ProcessProgram이 메모리에 적재되어 실행되고 있는 상태를 일컫는 용어
ThreadProcess내의 제어의 단일한 순차적 흐름(single sequential flow)
사용자 삽입 이미지
 각각의Process는 실행시 고유의메모리 공간을 점유하고 운영체제의 통제를 받아
다른 Process에 의해 간섭을 받지 않고 독립적으로 수행되지만Thread
Process가 점유한 메모리 공간에서 다른 Thread와병렬적으로 수행되게 됩니다.
사용자 삽입 이미지
 Thread는 자신이 수행할인스트럭션의 순서를 포함한최소의 자원만 가지고
있습니다. 이런 특징으로 인해 여러 개의 Process로 실행되는 경우보다
Multithread 프로그래밍을 하게 되면시스템 자원을 좀더효율적으로 이용할 수 있게
됩니다.
사용자 삽입 이미지
 자바에서 대부분의 프로그램은 개발자가 원하든 원하지 않든Multithread를 사용하게
됩니다.
사용자 삽입 이미지
 
사용자 삽입 이미지
사용자 삽입 이미지
 왼쪽 그림은 개발자가콘솔기반의 프로그램을 작성했을 경우 실행되는 모습입니다.
우측의 그림은 개발자가 명시적으로 Thread를 생성한 경우이거나, 또는 명시적으로
Thread를 생성하지 않은 경우라도 AWT나스윙을 사용한 경우는 자동적으로
Multithread 환경에서 작동하는 경우가 됩니다.

 

사용자 삽입 이미지
Thread의 동의어
사용자 삽입 이미지
 Thread는 다른 말로수행문맥(Execution context),
경량 프로세스(Lightweight process)
라고도 불리는 것을 참고로 알아두시기
바랍니다.

 

 
사용자 삽입 이미지
사용자 삽입 이미지
 
사용자 삽입 이미지
Thread 비유 예문
사용자 삽입 이미지

 

 

 여러분들의 이해를 돕기 위해 Thread와 Process의 개념에 잘 비유되는 예문을
소개합니다. 먼저 정리를 하면 이 예문에서개인서재Process의 개념과 일치하고,
시립도서관Multithread의 개념과 유사합니다.
사용자 삽입 이미지
 
사용자 삽입 이미지
개인서재
사용자 삽입 이미지
 
사용자 삽입 이미지
 나는 공부방에 조그마한 서재가 있습니다.
거기에는 그동안 모은 수십권의 책이 있지요.
그 책들은 내가 필요할 때마다 바로 꺼내어
읽어 볼 수 있습니다.
하지만 모든 종류의 책이 있는건 아니어서
가끔은 불편하기도 합니다.
사용자 삽입 이미지
사용자 삽입 이미지
시립도서관
사용자 삽입 이미지
 
사용자 삽입 이미지
 우리 동네에는 시립도서관이 있습니다.
거기에는 공부방의 서재와 비교도 할 수 없을
정도의 많은 책들이 있습니다.
하지만 가끔 필요한 책을 찾으면 이미
대출중이라서 며칠씩 기다려야 하는 경우도
있습니다.
사용자 삽입 이미지
 그렇다면 개인서재와 시립도서관의 특징은 무엇일까요?
사용자 삽입 이미지
 
개인서재시립도서관
사용자 삽입 이미지
책이 그리 많지 않다.
사용자 삽입 이미지
필요하면 언제나 모든 책을 볼 수
 있다.
사용자 삽입 이미지
상대적으로 많은 비용이 소요된다.
사용자 삽입 이미지
자신이 점유한 시스템의 리소스를
 
모두 혼자서 사용하는 
Process
 비유된다.
사용자 삽입 이미지
많은 책이 준비되어 있다.
사용자 삽입 이미지
대출중인 책을 보기 위해 기다려야
 한다.
사용자 삽입 이미지
효율성에 비하면 비교적 적은 비용이
 든다.
사용자 삽입 이미지
공유된 많은 자원을 여러 Thread와
 
공유해서 사용하는 
Multithread
 비슷하다.
사용자 삽입 이미지
 개인서재와 같이Process는 오로지 자신의 실행영역과 자원을 가지며,
오직 자신만이 내부의 자원을 사용하므로 Multithread 환경과 같이 다른 Thread에
자원을 빼앗길 염려는 없습니다. 하지만 모든 사람이 개인서재를 만드는 것처럼 많은
비용(시스템의 리소스)을 지출해야 합니다.
사용자 삽입 이미지
 반면에 시립도서관에 비유되는Multithread환경에서는 공유된 자원을
여러 Thread에서 사용하므로 동일한 비용을 들이더라도 좀더 효율적인 자원의 사용이
가능해집니다. 다만 다른 사람이 책을 빌려 간 상황이 발생하는 것처럼 자신이
필요한 자원을 다른 Thread가 점유하고 있다면 자원의 점유가 해제될 때까지
대기해야 하는 불편함은 있습니다.
사용자 삽입 이미지
사용자 삽입 이미지
 
사용자 삽입 이미지
왜 Thread를 사용할까?
사용자 삽입 이미지
 그렇다면 지금까지의 방법으로도 웬만한 프로그램 작성에는 별 문제가 없었는데
무엇 때문에 Thread까지 사용해서 개발자들을 어렵게 하는 걸까요?
크게 두 가지 관점에서 Thread 사용의 필요성을 정리할 수 있습니다.
사용자 삽입 이미지
 
사용자 삽입 이미지
첫째, 프로그램의 특성상 하나의 명령 제어만으로는 해결하기 힘든 문제가
 존재할 경우 이를 효과적으로 해결할 수 있습니다.
사용자 삽입 이미지
 앞에서 예를 든 것과 같이 GUI 프로그램에서 '실행' 버튼을 눌러 어떤 작업을
실행한 경우, 이 작업이 끝나기 전에는 다른 어떤 명령도 이 프로그램 내에서는
실행될 수가 없습니다.
사용자 삽입 이미지
 사용자가 '취소' 버튼을 아무리 눌러도 이 이벤트는 작동하지 않습니다.
물론 운영체제 내부에서 관리하는 이벤트 큐에는 저장이 되므로 작업이 끝난
이후에는 '취소' 버튼이 눌린 이벤트가 뒤늦게 실행이 됩니다.
사용자 삽입 이미지
 또 하나의 예로는 여러 클라이언트에 신속하게 응답을 해야 하는서버 프로그램
들 수 있습니다. 만약 Thread 기반으로 작성되지 않은 경우라면 마찬가지로
먼저 연결이 된 클라이언트가 작업을 마친 이후에나 다음 클라이언트의 요청을
처리할 수 있게 됩니다.
사용자 삽입 이미지
사용자 삽입 이미지
둘째, 시스템 자원의 효율적 이용측면을 들 수 있습니다.
사용자 삽입 이미지
 앞에서 예를 든 서버 프로그램의 경우 굳이 Thread 기반으로 작성하지 않아도
구현이 가능합니다. 즉, 서버측에서데몬(Daemon)프로그램을 통해 클라이언트의
요청을 받아들인 후, 별도의 프로세스를 실행시켜서 처리하는 방법 (예전의
C 프로그램에서 사용했던 fork()등의 방법)을 사용할 수도 있습니다.
사용자 삽입 이미지
 하지만 이 경우 프로세스의 특성상 Thread 기반의 처리보다시스템의 리소스
더 많이 차지한다는 단점을 감수해야 합니다.
사용자 삽입 이미지
 따라서 Thread를 사용해서 클라이언트의 요청을 처리하도록 프로그램을
작성한다면 훨씬 적은 시스템 리소스를 가지고 좀 더 많은 클라이언트를 수용할 수
있도록 할 수 있습니다.
사용자 삽입 이미지
 이상과 같은 이유에서 Thread는 수준높은 프로그램 작성에필수적고려사항
됩니다. 또한 자바는 언어적으로도 개발자가 쉽게 Thread 기반의 프로그램을
작성할 수 있는API를 제공함으로써 빠르고 안정적인 프로그램 작성을 도와줍니다.

사용자 삽입 이미지
데몬(Daemon)
사용자 삽입 이미지
 시스템의 어떤 서비스를 계속적으로 처리하기 위해 실행되는 프로그램이며,
대부분 자식 프로세스들을 계속적으로 생성시켜서 요청된 서비스들을
처리하도록 하고, 자신은 계속적으로 서비스 요청을 받을 수 있도록
종료되지 않고 대기하는 방식으로 작동합니다.

 

사용자 삽입 이미지
사용자 삽입 이미지
 
사용자 삽입 이미지
Thread의 특징
사용자 삽입 이미지
 그러면 기존의 다른 언어와 비교해서 자바의 Thread는 어떠한 특징이 있을까요?
사용자 삽입 이미지
 자바는언어 수준에서 Thread를 지원합니다. 다시 말해서, 일반적으로 다른 언어에서
Thread를 사용하기 위해서는 보조 라이브러리를 사용해야 하지만,
자바는 언어 수준에서 Thread를 사용하기 위한 클래스들이 준비되어 있기 때문에
좀더 쉽게 개발자가 Thread를 이용할 수 있습니다.
사용자 삽입 이미지
 Thread를 사용하기 위해 준비된 클래스들에는java.lang.Thread
java.lang.ThreadGroup이 있으며java.lang.Runnable인터페이스와 연관되어 있습니다.
또한 모든 클래스들의 조상 클래스인java.lang.Object에서 조차 Thread를
지원하기 위한 메쏘드가 준비되어 있습니다.
사용자 삽입 이미지
 이런 클래스와 인터페이스는 Thread 프로그래밍시 앞으로 자주 참조를 해야 하므로
한번쯤 자바 API 문서에서 찾아보시기 바랍니다. 자바로 Thread 프로그래밍을
한다는 것은 위의 클래스들을 개발자가 효율적으로 이용해서 코딩한다는 것을
의미합니다.
사용자 삽입 이미지
 
사용자 삽입 이미지
자바 API에 대해

 

 

사용자 삽입 이미지
자바 API에 대해
사용자 삽입 이미지
 JDK 디렉토리(예, C:\jdk1.3.1등)에 docs\api라는 폴더가 있을것입니다.
이곳의 index.html 파일이 바로 자바에서 제공하는 API를 정리한
문서입니다.
항상 자바로 프로그래밍을 할 때는 이 문서를 참조해야 합니다.

 

 

사용자 삽입 이미지
사용자 삽입 이미지
 그러면 이제 실제로 Thread를 만들어 보겠습니다.
자바에서 Thread를 생성하는 방법에는Thread 클래스를 상속받는 방법
Runnable 인터페이스를 구현하는 방법등 두 가지가 있습니다. 먼저 두 가지 방법으로
Thread를 작성해 보고, 각각의 특징에 대해 간단히 알아 보겠습니다.
사용자 삽입 이미지
 
사용자 삽입 이미지
Thread 클래스 상속받기
사용자 삽입 이미지
 먼저 java.lang.Thread 클래스를 상속받아 작성하는 방법입니다.
다음의 코드를 작성해서 실행시켜 보시기 바랍니다.
사용자 삽입 이미지
 
사용자 삽입 이미지
각각의 번호를 클릭하면 자세한 설명을 볼 수 있습니다.
사용자 삽입 이미지
 
// TestThread1.java: Thread 클래스를 상속받아 Thread를 생성하는 예제
사용자 삽입 이미지
사용자 삽입 이미지
public class TestThread1extends Thread{// Thread의 작업 내용을 기술하는 run() 메쏘드
사용자 삽입 이미지
public void run() {int i = 0; while(i++   10) { System.out.println ("Extended Thread testing...\t Step " + i); try {sleep(500);} catch(InterruptedException e) { } } }// 프로그램의 진입점 main() 메쏘드 public static void main(String args[]) {
사용자 삽입 이미지
Thread t = new TestThread1();
사용자 삽입 이미지
t.start();}}
사용자 삽입 이미지
 
사용자 삽입 이미지
TestThread1.java
사용자 삽입 이미지
 앞의 예제코드는 단순히 0.5초 지연되면서 지정된 문자열을 출력하는 코드입니다.
위의 코드에서 가장 중요한 부분은 Thread의 작업을 정의한run()메쏘드와 실제로
작업을 시작하는start()메쏘드입니다.
사용자 삽입 이미지
 자바 API 문서의 java.lang.Thread 부분을 보시면 Thread의 클래스 원형은
다음과 같이 선언되어 있습니다.
사용자 삽입 이미지
 
public class Thread extends Objectimplements Runnable
사용자 삽입 이미지
 이 선언문의 의미는, Thread라는 클래스는 Object 클래스(자바에서 모든 클래스의
조상 클래스)를 상속받았고, Runnable Interface를 implement했다는 말입니다.
그리고 Runnable Interface에는 run()이라는 오직 하나의 메쏘드만이 선언되어
있습니다.
사용자 삽입 이미지
 이런 이유에서 우리가 Thread 클래스를 상속받아 새로운 클래스를 만들기 위해서는
run()이라는 메쏘드를반드시 정의해야만 합니다. 다시 말해 Thread를 이용해
수행할 작업들은 바로 이 run() 메쏘드에서 구현되어야 하는 것입니다. 그리고 이를
사용하기 위해서는 작성된 클래스의 새로운 인스턴스를 생성하고 start() 메쏘드를
호출하는 것이 전부입니다.
사용자 삽입 이미지
 
사용자 삽입 이미지

 

// TestThread1.java: Thread 클래스를 상속받아 Thread를 생성하는 예제
public class TestThread1 extends Thread {

     // Thread의 작업 내용을 기술하는 run() 메쏘드
 public void run() {
  int i = 0;
  while(i++ < 10) {
   System.out.println("Extended Thread testing...\t Step " + i);
   try {
    sleep(500);
   } catch(InterruptedException e) { }
  }
 }

     // 프로그램의 진입점 main() 메쏘드
 public static void main(String args[]) {
  Thread t = new TestThread1();
  t.start();
 }

}

 

사용자 삽입 이미지
사용자 삽입 이미지
 
사용자 삽입 이미지
Runnable 인터페이스 implement 하기
사용자 삽입 이미지
 앞의 코드와 동일한 작업을 수행하는 코드를 Runnable 인터페이스를 이용해서
구현하였습니다. Thread를 상속받아 작성한 코드와의 차이점을 붉은색으로
표시했으니 주의 깊게 보시기 바랍니다.
사용자 삽입 이미지
 
사용자 삽입 이미지
각각의 번호를 클릭하시면 자세한 설명을 보실 수 있습니다.
사용자 삽입 이미지
 
// TestThread2.java: Runnable Interface를 implement받아 Thread를 생성하는
// 예제
사용자 삽입 이미지
사용자 삽입 이미지
public class TestThread2implements Runnable {public void run() { int i = 0; while(i++ < 10) { System.out.println("Implemented Thread testing...\t Step " + i); try {
사용자 삽입 이미지
Thread.sleep(500);} catch(InterruptedException e) { } } } public static void main(String args[]) {
사용자 삽입 이미지
Runnable r = new TestThread2(); Thread t = new Thread(r);t.start(); } }
사용자 삽입 이미지
 
사용자 삽입 이미지
TestThread2.java
사용자 삽입 이미지
 앞의 코드와 이번 코드는 run() 메쏘드를 구현한 것은 동일합니다.
하지만, 구현 방식에서는 위와 같은 차이점이 있습니다.
다시 한번 코드를 자세히 살펴보면서 확인하시기 바랍니다.
사용자 삽입 이미지
 
사용자 삽입 이미지

 

// TestThread2.java: Runnable Interface를 implement받아 Thread를 생성하는 예제
 public class TestThread2 implements Runnable {

 public void run() {
  int i = 0;
  while(i++ < 10) {
   System.out.println("Implemented Thread testing...\t Step " + i);
   try {
    Thread.sleep(500);
   } catch(InterruptedException e) { }
  }
 }

 public static void main(String args[]) {
  Runnable r = new TestThread2();
  Thread t = new Thread(r);
  t.start();
 }

}

 

 

사용자 삽입 이미지
사용자 삽입 이미지
 
사용자 삽입 이미지
두가지 방법의 특징
사용자 삽입 이미지
 다음은 앞에서 살펴 본 두가지 구현 방법의 차이점과 이점에 대해 알아 보겠습니다.
사용자 삽입 이미지
 
Runnable
인터페이스를
Implement 하는
경우
사용자 삽입 이미지
객체 지향적인 관점에서 좀더OOP다운 구현 방법임.
사용자 삽입 이미지
사용자 삽입 이미지
Thread는 자바의 언어적인 측면에서는 가상의 CPU를
 구현했다고 말함
사용자 삽입 이미지
사용자 삽입 이미지
따라서 이런 CPU 자원과 구현될 클래스를 구분하는
 방법은 좀더 언어적인 측면에서 '객체 지향적이다' 라고
말함
사용자 삽입 이미지
사용자 삽입 이미지
다중상속을 지원하지 않는 자바의 특성상다른 클래스를
 상속받아야 하고, Thread를 이용해야 하는 경우라면
반드시 Runnable 인터페이스를 Implement하여 사용해야 함
Thread
클래스를
상속해서
구현하는 경우
사용자 삽입 이미지
클래스 자체가 Thread를 상속받아 구현되기 때문에
 Thread 클래스에서 제공하는 static 메쏘드를
호출하기 위해서 Thread.sleep() 과 같이 구현하는 대신
this.sleep() 또는 좀더 줄여서 sleep() 등과 같이간결하게
코드를 작성
할 수 있는 이점이 있음.
사용자 삽입 이미지
사용자 삽입 이미지
많은 개발자가 간결한 코드 때문에 Thread 클래스를
 상속하는 방법을 많이 사용하지만, 자바가 다중상속을
지원하지 않는 관계로 특정 클래스 작성시다른 클래스를
상속해야 한다면
반드시 Runnable 인터페이스를
implement 하는 방법을 사용해야 함


사용자 삽입 이미지
사용자 삽입 이미지
 
사용자 삽입 이미지
Thread의 종료
사용자 삽입 이미지
 앞에서 살퍼본 바와 같이 Thread는 run() 메쏘드를 구현하고 start() 메쏘드를
호출하는 것으로 실행됩니다. 엄밀하게 말하면 실행된다기 보다는실행가능 상태
되는 것인데 이에 대해서는Thread의 상태전이에서 자세히 알아보겠습니다.
사용자 삽입 이미지
 그렇다면 Thread의 수행은 어떻게 종료시킬 수 있을까요?
JDK 1.2 이전에는 stop()이라는 메쏘드가 지원되어서 Thread를 중지시키기 위한
부분에서 호출해 사용할 수 있었습니다.
사용자 삽입 이미지
 하지만 이런 방법은 뒤에서 설명하게 될 Object의 Lock이나 Thread의 본질적인 개념에
관련해심각한 문제를 유발할 가능성이 있어서 버젼 1.2 이상에서는Deprecated되었고
더이상 사용하지 않게 되었습니다.
사용자 삽입 이미지
 따라서, Thread를 종료하는 유일한 방법은run() 메쏘드를 끝까지 진행시켜서
자연스럽게 종료
시키는 것입니다. 물론 작업을 한번 수행하고 종료하는 메쏘드라면
문제가 없겠지만 계속해서 반복작업을 해야 하는 경우라면 문제가 될 수 있습니다.
사용자 삽입 이미지
 다음의 코드를 봅시다.
사용자 삽입 이미지
 
public void run() { boolean isFinished = false; while (isFinished == true) { ... ... if (...) isFinished = true; } }
사용자 삽입 이미지
 만약 반복적인 작업을 수행하는 Thread의 run() 메쏘드를 구현해야 한다면 순환문을
벗어나 Thread가 정상 종료되지 못하고 무한반복을 할 위험이 있습니다.
사용자 삽입 이미지
 이런 경우는 앞의 예와 같이 Thread의 종료여부를 알려주도록 isFinished라는
boolean 타입의 변수를 먼저 선언하고 순환문 내부에 특정한 조건이 만족되면
이 변수의 값을 false로 변경합니다.
사용자 삽입 이미지
 이제 순환문이 다시 시작되는 시점에 isFinished값이 false로 되어 조건을 만족하지
않으므로 순환문을 벋어나게 됩니다.
이런 변수를조건변수라고 이 방법은 Thread에서 순환문을 사용시 자주 사용하는
방법입니다.
사용자 삽입 이미지
 혹시 지금까지의 학습 내용 중에서 다음과 같은 의문점이 생기시는 분들이 계신지
모르겠군요.
사용자 삽입 이미지
 "Thread를 사용하면 한번에 여러 job을 수행할 수 있다고 했는데, 지금까지의 예제는
기존에 작성하던 프로그램과 별로 틀린 게 없지 않나요?"
사용자 삽입 이미지
 맞습니다. 앞의 예제들은 여러분들의 이해를 돕기위해 한번에 하나의 Thread만
생성시켰습니다. 먼저 이런 간단한 예제에 익숙해지고 난 후에 약간의 수정과 확장을
하게 되면 실제로 우리가 생각했던 Multithread를 만드는 것은 별로 어렵지 않습니다.
사용자 삽입 이미지
 계속해서 다음 절에서는 이번 절에 학습한 Thread 기본지식을 가지고 Thread의
깊은 곳을 들여다 보도록 하겠습니다.

 
사용자 삽입 이미지
Deprecated
사용자 삽입 이미지
 자바는 수 십년 간 사용되어온 다른 언어에 비하면 매우 젊은 언어입니다.
다른 측면에서는 아직도 개선하고 수정되어야 할 부분이 많다는
의미입니다. Deprecated 되었다는 말은 현재는 사용할 수 있지만 자바의
개선을 위해 차후 지원하지 않겠다는 의미입니다.
사용자 삽입 이미지
 따라서 버젼에 관계없이 정상적으로 실행되는 프로그램을 작성하려면
Deprecated된 메쏘드나 클래스는 사용하지 않는 것이 바람직합니다.
사용자 삽입 이미지
 혹 컴파일 중에 Deprecated 관련된 Warning이 출력되면
javac -deprecation xxx.java 와 같이 컴파일 옵션을 주어서 소스 중에
Deprecated된 부분을 확인할 수 있습니다.

사용자 삽입 이미지
조건변수(Condition Variable)
사용자 삽입 이미지
 Thread의종료시점을 알려주기 위해 순환문 등에서 참조하도록 설정하는
변수

 

 

사용자 삽입 이미지
사용자 삽입 이미지

 
사용자 삽입 이미지
상태전이도
사용자 삽입 이미지

 앞절의 예제에서 Thread를 실행시키기 위해서 t.start() 메쏘드를 호출했던 것을
기억하실 겁니다. 실제로 Thread는 이 메쏘드의 호출과 동시에 run() 메쏘드가
시작되고 다양한 상태로 전이되면서 작업을 수행하다가 run() 메쏘드의 끝까지
실행된 후에 종료됩니다.
다음의 상태전이도는 Thread를 이해하는데 매우 중요하므로 상세히 살펴보도록
하겠습니다.
사용자 삽입 이미지
 
사용자 삽입 이미지

사용자 삽입 이미지
 
사용자 삽입 이미지
생성
사용자 삽입 이미지
 앞절의 예제에서 맨 처음 우리는 Thread를 사용하기 위해
t = new Thread() 와 같이 새로운 Thread를 생성했습니다.
바로 이렇게 해서 새로운Thread가 생성된 상태입니다.
사용자 삽입 이미지
실행가능상태
사용자 삽입 이미지
 생성된 Thread의 t.start() 메쏘드를 호출하면 실행 가능한 상태로
전이됩니다.
여기서 정확히 이해해야 할 것은 Thread가실행 가능한 상태가 되었어도
이때부터작업을 수행하지는 않는다는 것입니다.
사용자 삽입 이미지
실행중
사용자 삽입 이미지
 각 운영체제별로 구현된Scheduler에 의해 실행 가능한 상태에서
실제 실행 중인 상태로 전이됩니다.
Scheduler에 대한 내용은 뒤에 상세히 알아보도록 하겠습니다.
사용자 삽입 이미지
실행대기 상태
사용자 삽입 이미지
 만일 하나의 프로세스 내에 여러개의 Thread가 실행 중이라면
모든 Thread들에게 골고루 실행 기회를 주기 위해 실행 중인 하나의
Thread 이외에는 모두 실행대기 상태가 되고 다시 대기 중인 상태의
Thread중에서 하나가 실행되고 나머지는 대기하는 식으로 작업이
반복됩니다.
사용자 삽입 이미지
종료
사용자 삽입 이미지
 run()에 구현된 작업이 순차적으로 수행되어 메쏘드의 끝까지 실행이
완료되면 Thread는정상종료가 됩니다.
사용자 삽입 이미지
 대부분의 경우, Thread는 동시에 여러개가 생성되어 수행되고 이로 인해 컴퓨터에
있는 자원을 사용하기 위한경쟁을 하게 됩니다. 실제로는 하나의 CPU가 장착된 경우,
한번에 하나의 Thread만이 실행되게 됩니다.
사용자 삽입 이미지
 다음은 이런 Thread들의 경쟁과 자원의 점유를 통해 실행되는 상황을 개념적으로
표시한 그림입니다.
사용자 삽입 이미지
 
사용자 삽입 이미지
사용자 삽입 이미지
 이런 이유 때문에 개발자는 여러 개의 Thread가 균등하게 실행되어 원활히 작업을
수행할 수 있도록 배려를 해야 합니다. 만약 개발자가 이러한 배려를 해주지 않는다면
Thread의 수행순서는 전적으로운영체제에 맡겨져 진행되고, 최악의 경우,
어떤 Thread는 아예 실행될 기회도 얻을 수 없게 됩니다.
이 문제는 뒤에 나올 Thread의 Scheduling 부분에서 상세히 알아보겠습니다.
사용자 삽입 이미지
 Thread의 사용에 있어서 점점 복잡도가 증가하면서 개발자는 실제 어떤 순서대로
Thread가 특정자원을 점유하고 작업을 진행할지 예측하기가 불가능해지기도 합니다.
바로 이 문제 때문에 효율적인 Thread의 작성이 쉽지 않은 것입니다.


 

사용자 삽입 이미지
사용자 삽입 이미지
 다음으로 Thread 클래스에서 제공하는 각종 메쏘드들을 알아보겠습니다.
사용자 삽입 이미지
 
사용자 삽입 이미지
Thread 관련 Method
사용자 삽입 이미지
 이번에는 Thread를 효율적으로 사용하기 위해 Thread 클래스에서 제공하는 중요한
메쏘드들에 대해 알아보겠습니다. (메쏘드의 전체 리스트는 자바 API 문서를
참조하세요.)
사용자 삽입 이미지
 
메쏘드설명
run()Thread가 Runnable Interface를 implement한 구조이므로
반드시 구현해야 하는 핵심적인 메쏘드이며 작성할
Thread가 실행할 Action을 이 메쏘드에 정의합니다.
start()해당 Thread를 생성시킨 후 실제로 실행시키는 메쏘드이며,
실제로 JVM은 이 메쏘드가 호출되면 Thread에 정의된
run() 메쏘드를 실행하게 됩니다.
sleep(long millis)millis만큼의 시간동안 현재 실행중인 Thread의 실행을
잠시 멈추어 제어권을 대기중인 다른 Thread에게
넘겨줍니다.
yield()sleep과 동일하게 작동하나,sleep()이 자신보다 낮은
우선권(Priority)을 갖는Thread에게도 실행기회를 주는데
반해 yield()는 자신과 동일한우선권(Priority)을 갖는
Thread에게만 실행기회를 줍니다.
isAlive()Thread가 종료되었는지 점검합니다. run() 메쏘드의 끝까지
실행되어 종료되었으면 false를 리턴하고, 실행 중이거나
대기상태 등 아직 종료되지 않은 경우는 true를 리턴합니다.
join()호출한 Thread가 실행되어 종료될 때까지 대기합니다.
사용자 삽입 이미지
 다음은 Object 클래스(자바의 모든 클래스들의 조상)에 구현되어 Thread의 사용을
도와주는 메쏘드입니다.
사용자 삽입 이미지
 
메쏘드설명
wait()사용하려는 자원을 점유한 Thread가 제어를 놓을 때까지
기다립니다.
notify()제어를 놓기를 기다리는 Thread들에게 자원의 사용이
끝났음을 통보합니다.
사용자 삽입 이미지
 메쏘드들의 간단한 설명만으로는 이해하는데 한계가 있습니다. 앞으로 학습을
진행하면서 메쏘드가 계속 사용될 때 위의 설명을 다시 한번 참조하시기 바랍니다.
이외에도 Thread 클래스에는 많은 메쏘드들이 제공됩니다. 항상 API 문서를 보며
필요한 메쏘드들을 참조하는 것 잊지 마시기 바랍니다.
사용자 삽입 이미지
 다음은 JDK 1.1 이전에 사용하다가 Deprecate되어서 1.2 버젼 이후에는 사용하지 않는
메쏘드들입니다. 뒤에서 설명할 Object Lock이나 동기화에 문제가 있을 수 있으므로
앞으로는사용해서는 안됩니다.
사용자 삽입 이미지
 
메쏘드설명
resume()notify()로 대체
stop()모든 Thread는 run() 메쏘드의 종료와 함께 자연적으로
종료되도록 해야 함
suspend()wait()으로 대체

사용자 삽입 이미지
사용자 삽입 이미지
 
사용자 삽입 이미지
Scheduling이란?
사용자 삽입 이미지
 앞에서 언급된 것처럼 Thread가 한정적인 자원을 효율적으로 이용하게 하려면
개발자가 Thread의 실행순서를 잘 설정해 주어야 합니다.
사용자 삽입 이미지
 이런 과정이 없다면 실제 Thread의 실행순서는 운영체제에 구현된 Scheduler에 의해
관리되므로 개발자가원하지 않는 결과를 초래할 수도 있습니다.
바로 이런 이유 때문에 개발자는 Thread들의우선순위자원을 공유할 수 있도록
배려를 해야 합니다. 이 과정을Thread Scheduling이라고 합니다.
사용자 삽입 이미지
 자바에서는 Thread의 Scheduling을 위해 두가지 Method를 제공합니다.
사용자 삽입 이미지
 
메쏘드설명
setPriority
(int newPriority)
Thread의 실행 우선순위를 newPriority로 설정합니다.
우선순위의 범위는 Thread의 static 상수에 정의된 대로
MAX_PRIORITY, MIN_PRIORITY사이의 값이어야 합니다.
getPriority()현재 Thread의 우선순위를 리턴합니다.
사용자 삽입 이미지
 또한 Thread에 정의된 상수는 우선순위에 관련된 것으로 다음의 세가지가 있습니다.
사용자 삽입 이미지
 
상수설명
MAX_PRIORITYThread의 우선순위 중 가장 큰 값 (10)
NORM_PRIORITYThread의 우선순위 중 가장 중간 값 (5)
MIN_PRIORITYThread의 우선순위 중 가장 작은 값 (1)
사용자 삽입 이미지
 앞에 Thread관련 메쏘드에 설명드린 대로 yield() 메쏘드는 동일한 Priority를 갖는
Thread에게 실행할 수 있는 기회를 주는 반면, sleep() 메쏘드는 자신보다
낮은 Priority의 Thread에게도 실행 기회를 준다는 것을 다시 한번 기억하시기 바랍니다.

사용자 삽입 이미지
사용자 삽입 이미지
 
사용자 삽입 이미지
Scheduling 테스트
사용자 삽입 이미지
 그렇다면 개발자가 Scheduling을 고려하지 않은 Thread가 어떻게 작동하는지
예제를 통해 알아보도록 하겠습니다. 다음의 예제를 작성하여 실행결과를 보시기
바랍니다.
사용자 삽입 이미지
 
// TestThread3.java: 이기적인 Thread 구현 public class TestThread3 extends Thread { // 이름을 갖는 Thread 생성 public TestThread3(String name) { super(name); } // Thread가 수행할 작업 정의 public void run() { int i = 0; while(i++ < 10) { System.out.println(getName() + " Thread...\t Step " + i) } } // 프로그램의 제어 public static void main(String args[]) { Thread t1 = new TestThread3("#1"); Thread t2 = new TestThread3("#2"); t1.start(); t2.start(); } }
사용자 삽입 이미지
 
사용자 삽입 이미지
TestThread3.java
사용자 삽입 이미지
 앞에서 사용한 예제를 조금 수정해서 두개의 Thread를 #1, #2라는 이름을 주어서
생성시켰습니다. 과연 두개의 Thread가 과연 어떤 순서로 수행이 될까요?
 
사용자 삽입 이미지
사용자 삽입 이미지
 
사용자 삽입 이미지

// TestThread3.java: 이기적인 Thread 구현
public class TestThread3 extends Thread {

     // 이름을 갖는 Thread 생성
 public TestThread3(String name) {
  super(name);
 }

     // Thread가 수행할 작업 정의
 public void run() {
  int i = 0;
  while(i++ < 10) {
   System.out.println(getName() + " Thread...\t Step " + i);
  }
 }

     // 프로그램의 제어
 public static void main(String args[]) {
  Thread t1 = new TestThread3("#1");
  Thread t2 = new TestThread3("#2");
  t1.start();
  t2.start();
 }

}




사용자 삽입 이미지
사용자 삽입 이미지
 
사용자 삽입 이미지
운영체제별 Thread 모델
사용자 삽입 이미지
 그렇다면 모든 Platform에서 동일한 결과가 나올까요?
사용자 삽입 이미지
 그렇지 않습니다. 만약 Linux 기반의 Platform에서 예제를 실행시킨다면 다음의
결과처럼 #1 Thread가 모두 실행이 완료가 되고, 그후에 #2 Thread가 실행되는 것을
알 수 있습니다.
사용자 삽입 이미지
 
#1 Thread... Step 1 #1 Thread... Step 2 #1 Thread... Step 3 #1 Thread... Step 4 #1 Thread... Step 5 #1 Thread... Step 6 #1 Thread... Step 7 #1 Thread... Step 8 #1 Thread... Step 9 #1 Thread... Step 10 #2 Thread... Step 1 #2 Thread... Step 2 #2 Thread... Step 3 #2 Thread... Step 4 #2 Thread... Step 5 #2 Thread... Step 6 #2 Thread... Step 7 #2 Thread... Step 8 #2 Thread... Step 9 #2 Thread... Step 10
사용자 삽입 이미지
 이는 각 운영체제에서 구현한Thread 모델이 서로 달라서 발생하는 현상이며,
Thread 모델 유형과 각각의 차이점은 뒤에서 좀더 자세히 알아보겠습니다.
사용자 삽입 이미지
 그렇다면 이렇게 운영체제 마다 다른 결과가 개발자에게는 어떤 의미가 있을까요?
답은 자바의WORA(Write Once Run Anywhere)의 개념에 충실하도록
어느 Platform에서나 동일한 결과를 얻을 수 있는 프로그램을 작성해야 한다는
것입니다.
사용자 삽입 이미지
 위의 예제는 Thread의 Schedule 통제를운영체제에 위임한 경우입니다.
위와 같은 코딩이 문제가 없는 경우도 있겠지만 대부분의 경우 개발자의 의도와는
다르게 실행됩니다. 따라서 Thread 작성시는 반드시 모든 Thread가 골고루
실행기회를 갖도록 하는게 중요합니다.


 
사용자 삽입 이미지
사용자 삽입 이미지
 
사용자 삽입 이미지
운영체제에 무관한 Thread 만들기
사용자 삽입 이미지
 이번에는 앞의 예제를 조금 수정해서 운영체제에 무관하게 두개의 Thread가 골고루
수행될 수 있도록 만들어 보겠습니다.
사용자 삽입 이미지
 
// TestThread4.java: 운영체제에 무관한 Thread 구현 public class TestThread4 extends Thread { // 이름을 갖는 Thread 생성 public TestThread4(String name) { super(name); } // Thread의 수행작업을 구현 public void run() { int i = 0; while(i++ < 10) { System.out.println(getName() + " Thread...\t Step " + i);yield();} } // 프로그램의 제어 public static void main(String args[]) { Thread t1 = new TestThread4("#1"); Thread t2 = new TestThread4("#2"); t1.start(); t2.start(); } }
사용자 삽입 이미지
 
사용자 삽입 이미지
TestThread4.java
사용자 삽입 이미지
 Thread가 한번의 Action, 즉 System.out.println()을 실행한 후에는 현재 대기중인
다른 Thread가 실행될 기회를 가질 수 있도록yield() 메쏘드를 호출하도록
수정했습니다. 다음은 이렇게 수정한 후의 실행 결과입니다.
사용자 삽입 이미지
 
사용자 삽입 이미지

// TestThread4.java: 운영체제에 무관한 Thread 구현
public class TestThread4 extends Thread {

     // 이름을 갖는 Thread 생성
 public TestThread4(String name) {
  super(name);
 }

     // Thread의 수행작업을 구현
 public void run() {
  int i = 0;
  while(i++ < 10) {
   System.out.println(getName() + " Thread...\t Step " + i);
   yield();
  }
 }

     // 프로그램의 제어
 public static void main(String args[]) {
  Thread t1 = new TestThread4("#1");
  Thread t2 = new TestThread4("#2");
  t1.start();
  t2.start();
 }

}


사용자 삽입 이미지
사용자 삽입 이미지
 
사용자 삽입 이미지
Thread 모델
사용자 삽입 이미지
 다음으로 앞에서 잠시 언급한Thread 모델에 관해 알아보겠습니다.
Thread가 운영체제에서 구현된 방식은 크게협력형선점형 모델로 나눌 수 있습니다.
각 구현방식의 특징과 장단점에 대해 알아보겠습니다.
사용자 삽입 이미지
 
구분협력형(Corporative)선점형(Preemptive)
특징
사용자 삽입 이미지
제어권을 잡은Thread가 실행이
 끝날 때가지 CPU의 제어권을
갖음.
사용자 삽입 이미지
운영체제 레벨에 구현된 Timer가
 일정주기의Time slice마다
현재 작동 중인 Thread의 제어를
빼앗아 다른 Thread에게 넘겨줌
장점
사용자 삽입 이미지
매우 빠르며
 컨텍스트 스왑(Context Swap)
의한 오버헤드가 적음
사용자 삽입 이미지
프로그래밍이 쉬워짐
사용자 삽입 이미지
기아현상(Starvation)이 발생할
 소지가 적음
사용자 삽입 이미지
병렬적인 처리가 가능해짐
단점
사용자 삽입 이미지
특정 Thread가
 기아현상(Starvation)이
일어나지 않도록 개발자가
고려해 주어야 함
사용자 삽입 이미지
병렬적으로 작동할 수 없음
 즉, 여러 개의 CPU가 장착되어
있어도 CPU별로 동시에
다른 작업을 수행하게 할 수 없음
사용자 삽입 이미지
개발자가 Thread의 작업을
 나누어 주어야 하기 때문에
코드가 매우 복잡해 질 수 있음
사용자 삽입 이미지
Thread의 관리가 운영체제
 레벨에서 되므로비효율적
사용자 삽입 이미지
컨텍스트 스위치를 위한
 부하가 많이 걸림
사용자 삽입 이미지
 실제로 이런 Thread 모델은 운영체제별로 Virtual Machine에서 상이한 구현방식을
사용하기 때문에 개발자가 이에 대한 변경을 할 수는 없습니다.
참고로 Windows 기반의 VM에서는선점형 방식을 사용하고, Mac VM은협력형
사용합니다. Unix의 경우는두가지 방법이 혼합된 경우가 많습니다.
사용자 삽입 이미지
 앞의 TestThread3.java의 예제 결과에서 본대로 운영체제별로 이런 특징 때문에
Thread간에 제어가 자동으로 넘어가기도 하고, 그렇지 못하기도 하다는 것을
이해하시기 바랍니다. 또한 개발자는 이러한 특성을 감안하여운영체제에
종속적이지 않은 Thread 프로그램을 작성
해야 한다는 것도 잊지 마세요.
사용자 삽입 이미지
 
사용자 삽입 이미지
Green thread, Native thread

 

사용자 삽입 이미지
Green thread
사용자 삽입 이미지
 Unix 기반의 Platform에서 협력형 모델에 가깝게 구현된 스케쥴링 방식
사용자 삽입 이미지
사용자 삽입 이미지
Native thread
사용자 삽입 이미지
 Unix 기반의 Platform에서 선점형 모델에 가깝게 구현된 스케쥴링 방식

사용자 삽입 이미지
컨텍스트 스왑(Context Swap)
사용자 삽입 이미지
 현재 실행중인 프로세스를 교체하는 작업

사용자 삽입 이미지
기아현상(Starvation)
사용자 삽입 이미지
 우선권에서 밀리는 특정 Thread가 제어권을 얻지 못해 실행되지 못하는
현상

 

 

사용자 삽입 이미지
사용자 삽입 이미지

 
사용자 삽입 이미지
Thread의 생성 한계
사용자 삽입 이미지

 Thread는 Process보다 적은 리소스를 점유한다고 했는데 그렇다면 과연 Thread는
몇 개까지 만들 수 있을까요?
사용자 삽입 이미지
 저도 궁금해서 테스트를 해보니 384Mbyte의 Windows ME 환경에서 약 1200개 정도,
2000 Pro에서는 256Mbyte에서 7000개까지 Thread가 생성되고 OutOfMemoryError가
발생했습니다. 이런 부분에서 확실히 Windows 2000이 이전 버젼보다는 안정되었다는
느낌이 드는군요.
사용자 삽입 이미지
 일단 제가 테스트해본 결과로는 결국 메모리라는 시스템 리소스의 한계가
Thread 생성의 한계로 작용했습니다. 실제 7000개 이상의 Thread가 필요할 정도의
시스템이 어디에 쓰일지는 여러분의 상상에 맡기겠습니다.
사용자 삽입 이미지
 여러분들의 PC 성능이 궁금하신 분들은 다음의 코드를 실행시켜서 테스트 해 보시기
바랍니다. 아무일도 하지 않고 60초간 sleep하는 Thread를 무한대로 생성시킵니다.
사용자 삽입 이미지
 
// ThreadCntTest.java: Thread의 생성 가능 개수 테스트 public class ThreadCntTest extends Thread { public void run() { while(true) { try { sleep(60000); } catch (InterruptedException e) {} } } public static void main(String args[]) { int i = 0; while(true) { new ThreadCntTest().start(); i++; if(i % 100 == 0) System.out.println("Thread Number: " + i); } } }
사용자 삽입 이미지
 
사용자 삽입 이미지
ThreadCntTest.java
사용자 삽입 이미지
 
사용자 삽입 이미지

 

// ThreadCntTest.java: Thread의 생성 가능 개수 테스트
public class ThreadCntTest extends Thread {

 public void run() {
  while(true) {
   try {
    sleep(60000);
   } catch (InterruptedException e) {}
  }
 }

 public static void main(String args[]) {
  int i = 0;
  while(true) {
   new ThreadCntTest().start();
   i++;
   if(i % 100 == 0)
    System.out.println("Thread Number: " + i);
  }
 }
}


사용자 삽입 이미지
 Thread의 개념 중 마지막으로 가장 중요한동기화에 대해 알아보겠습니다.
그동안 학습한 Thread의 상태와 Scheduling과 더불어 가장 중요한 개념이면서 동시에 가장
구현하기 힘든 부분이 바로 동기화입니다.
이제 마지막 고지를 향해 오른다는 각오로 이번절의 학습에 임해주시길 부탁합니다.
사용자 삽입 이미지
 만약 아무런 통제없이 시스템의 리소스를 점유하기 위해 여러 Thread가 경쟁한다면 어떤
상황이 발생할까요?
아마도 한권의 책을 대출하기 위해 여러명이 동시에 책을 잡고 잡아 당겨 책이 파손되는
것처럼 시스템의 리소스도 정상작동을 할 수 없게 될 것입니다.
사용자 삽입 이미지
 이런 문제를 해결하기 위해 자바의 언어적 측면에서 지원되는 기능이 바로동기화,
synchronized 키워드입니다.
먼저 동기화에 관련된 개념에 대해 정리를 하고 실제 동기화 구문을 작성하는 예제를 만들어
보겠습니다.

'Programming > JAVA' 카테고리의 다른 글

[펌] [SWING]JFileChooser  (0) 2005.06.29
[펌] 서버 소켓 예제  (0) 2005.06.24
[펌] 클라이언트 소켓  (0) 2005.06.24
[펌] 13. 쓰레드(Thread)  (0) 2005.06.23
[펌] Swing  (0) 2005.06.17

Posted by 영웅기삼
,