제8장 쓰레드 프로그래밍
쓰레드 기초
쓰레드란 무엇인가? 프로세스(process) 쓰레드(Thread) 실행중인 프로그램(program in execution) "실행될 명령어들의 연속(흐름) (a sequence of instructions to be executed)” 단일 쓰레드 프로그램 (single-thread program) 하나의 실행 흐름을 갖는 프로그램 다중 쓰레드 프로그램 (multi-thread program) 프로그램에서 여러 개의 함수들이 동시에 독립적으로 수행
쓰레드란 무엇인가? 단일 쓰레드 프로그램 다중 쓰레드 프로그램
쓰레드 실행 실행되는 명령어들의 순서를 선으로 표현 단일 쓰레드 프로그램 다중 쓰레드 프로그램
쓰레드 구현 및 실행 자바에서의 쓰레드 구현 쓰레드 실행 Thread 클래스로부터 상속 받아서 사용 Runnable 인터페이스를 구현해서 사용 상황에 맞게 선택하여 사용 가능 구현 run() 함수 구현 run() 함수에 쓰레드가 일할 내용을 기술 쓰레드 실행 쓰레드 객체를 만들고 start() 함수를 호출 start() 함수를 호출하면 자동적으로 run() 함수가 수행된다.
쓰레드 예제:상속 기존 클래스와 별개의 쓰레드를 정의하는 경우 예제 : ExtendedThread.java 2 1 class MyThread extends Thread { 2 3 public void run() { 4 System.out.println("Hello, I ("+getName()+") am working."); 5 } 6 } 7 8 public class ExtendedThread { 9 static public void main(String args[]) { 10 MyThread thr_a, thr_b; 11 12 thr_a = new MyThread(); 13 thr_b = new MyThread(); 15 thr_a.start(); 16 thr_b.start(); ………...
쓰레드 예제: 인터페이스 구현 예제 : RunnableThread.java 한 클래스 내에 쓰레드 구현이 필요한 경우에 많이 사용 예제 : RunnableThread.java 1 public class RunnableThread implements Runnable { 2 private int a = 10; 3 Thread thread1, thread2; 4 5 public RunnableThread() { 6 thread1 = new Thread(this); 7 thread1.start(); 8 } …... 16 public void run() { 19 } …….
쓰레드 생성 방법 Thread 클래스를 상속 받는 방법 Runnable 인터페이스를 구현하는 방법 클래스와 별개의 독립된 일을 하거나 여러 개의 쓰레드가 필요한 경우 Runnable 인터페이스를 구현하는 방법 클래스의 특성을 그대로 유지하면서 쓰레드를 하나 더 생성할 수 있음 Thread 클래스를 상속 받는 경우 Runnable을 구현하는 경우
쓰레드 상태도 자바 쓰레드의 상태도
쓰레드 종료 Dead 상태 Dead 상태는 쓰레드의 모든 작업이 종료된 상태 run()를 마치거나, stop()이 호출되면 Dead 상태로 전이 Dead 상태의 쓰레드는 start() 함수로 다시 실행시킬 수 없다. JDK 1.2 이후 버전에서는 쓰레드의 stop(), resume(), suspend() 메소드가 deprecated 경고를 유발 이러한 메소드들은 쓰레드에서 deadlock을 유발 가능 복잡한 시스템이나 상업용 시스템에서는 사용을 자제해야 함
suspend() / resume() suspend() resume() sleep() Runnable 상태의 쓰레드를 Blocked 상태로 만든다 resume() Blocked 상태의 쓰레드를 다시 Runnable 상태로 만든다. sleep() 일정 시간 동안 쓰레드를 Blocked 상태로 만든다.
스케쥴링 쓰레드의 스케쥴링은 JVM에서 이루어진다. 선점 방식 우선 순위에 따라 선점 (preemtive) 방식으로 스케쥴링 우선 순위가 높은 쓰레드가 등장하면 현재 실행중인 쓰레드는 쫓겨나고 우선순위가 높은 쓰레드가 실행된다. 우선 순위가 낮은 쓰레드는 우선 순위가 높은 쓰레드가 종료되거나 블락될 때까지 기다려야 한다
우선 순위: 예제 예제 : Priority.java 1 class MyThread5 extends Thread { 2 protected boolean stop; 3 ………………….. 21 public class Priority { 22 static public void main(String s[]) { 23 MyThread5 thr_a = new MyThread5("A"); 24 MyThread5 thr_b = new MyThread5("B"); 25 MyThread5 thr_c = new MyThread5("C"); 26 27 Thread.currentThread().setPriority(Thread.MAX_PRIORITY); 28 29 System.out.println("Starting the threads..."); 30 thr_a.start(); 31 thr_b.start(); 32 thr_c.start(); 33 34 try { 35 Thread.sleep(1000); 36 } catch (InterruptedException e) 37 38 System.out.println("Setting Thread b to a higher priority..."); 39 thr_b.setPriority(thr_b.getPriority() + 2); …………………….
데몬 쓰레드 다중 쓰레드 응용 프로그램은 모든 쓰레드가 종료되어야 프로세스가 종료 데몬 쓰레드 다른 쓰레드에 서비스를 해주면서 다른 쓰레드가 모두 종료하면 자신도 종료하는 쓰레드 데몬 쓰레드는 쓰레드를 생성한 다음에 setDaemon(true) 메소드를 이용해서 만들어줄 수 있다
데몬 쓰레드: 예 예제 : DaemonThread.java 2 protected boolean stop; 3 ….. 1 class MyThread6 extends Thread { 2 protected boolean stop; 3 ….. 13 if (isDaemon()) …... 20 21 public class DaemonThread { 22 static public void main(String s[]) { 23 MyThread6 thr_a = new MyThread6(); 24 MyThread6 thr_b = new MyThread6(); ……. 28 thr_b.setDaemon(true); 29 thr_b.start(); 30 31 try { 32 Thread.sleep(2000); 33 } catch (InterruptedException e) 34 35 System.out.println("Stopping the normal thread..."); 36 thr_a.stop(true); 37 } 38 }
쓰레드 응용
동기화(synchronization) 한 쓰레드가 a를 사용할 때 다른 쓰레드는 작업이 끝날 때까지 사용하지 못하도록 락(lock)을 걸고 작업 a=10; a = a + 1; synchronized 문장을 이용해서 메소드나 객체에 락(lock)을 걸 수 있다. synchronized void m( ) { … } Target object에 lock이 걸린다. 따라서 동시에 여러 개의 m()이 실행될 수 없다!
쓰레드 그룹 여러 쓰레드를 동시에 처리해야 하는 경우 어떤 조건을 만족하는 모든 쓰레드를 종료/블락 하는 경우 여러 쓰레드를 동시에 제어하기 위해서 쓰레드 그룹을 사용
쓰레드 그룹:예 예제 : ThrGroup.java 1 class MyThread extends Thread { 2 MyThread(ThreadGroup tg, String name) { 3 super(tg, name); 4 } …………………. 16 public class ThrGroup { 17 static public void main(String s[]) { 18 ThreadGroup MyGroup = new ThreadGroup("My Group"); 19 20 MyThread thr_a = new MyThread(MyGroup, "A"); 21 MyThread thr_b = new MyThread(MyGroup, "B"); 22 MyThread thr_c = new MyThread(MyGroup, "C"); 23 24 System.out.println("Starting the threads..."); 25 thr_a.start(); 26 thr_b.start(); 27 thr_c.start(); 28 29 try { 30 Thread.sleep(2000); 31 } catch (InterruptedException e) 32 33 System.out.println("Stopping the thread group..."); 34 MyGroup.stop(); 35 } 36 }
자바 쓰레드 자바의 쓰레드는 운영체제의 쓰레드로 매핑된다. 실제 운영체제에서 쓰레드 운영체제에 따라 쓰레드를 지원하는 방식이 다르다. 자바 쓰레드와 운영체제의 쓰레드와 매핑이 달라질 수 있다. 자바 쓰레드는 일종의 사용자 쓰레드이다. 실제 운영체제에서 쓰레드 사용자 쓰레드 사용자 쓰레드는 사용자가 라이브러리를 이용해서 만든 쓰레드 커널 쓰레드로 커널 쓰레드는 OS에서 실제 스케쥴링이 일어나는 단위 사용자 쓰레드와 커널 쓰레드는 M-1 관계, 1-1 관계, M-N 관계를 가짐
M-1 매핑 자바 쓰레드는 사용자 쓰레드와 1-1 관계를 갖고, 사용자 쓰레드는 커널 쓰레드와 M-1 관계를 갖는다. 커널 쓰레드가 하나만 있으면 쓰레드를 이용할 때의 성능 향상은 어렵다
M-1 매핑 M-1 매핑
1-1 매핑 1-1 매핑 관계는 NT, Win2000에서 사용되는 쓰레드 자바 쓰레드 1개에 커널 쓰레드가 1개 매핑된다. CPU에서 스케쥴링 단위는 커널 쓰레드 하나의 프로그램이 여러 커널 쓰레드 자바 프로그램에서 성능 향상을 기대 1-1 매핑
M-N 매핑 NT의 1-1 매핑보다 프로그램하는데 편리하면서, 다중 쓰레드의 장점을 활용할 수 있다. Solaris 운영체제
자바 쓰레드와 POSIX 쓰레드 비교
운영체제별 쓰레드 환경
쓰레드 예: Ticker 테이프 애플릿이나 응용프로그램에서 글자가 조금씩 이동하는 애니메이션 프로그램 Runnable 인터페이스를 구현한 쓰레드를 작성 예제 글자를 조금씩 이동시키기 위해 쓰레드가 필요하다. Ticker 클래스는 글자를 그려주기 위해서 Canvas 클래스로부터 상속 받고, 쓰레드도 필요하다. 이 경우 Runnable 인터페이스를 구현하는 것이 효율적
쓰레드 예: Ticker.java 1 import java.awt.*; 2 import java.applet.*; 3 4 public class Ticker extends Canvas implements Runnable { 5 Font font; 6 FontMetrics metrics; 7 Thread runner; 8 int currentx; 9 int currenty; 10 Image offscreenImg; 11 Graphics offscreenG; ………... 21 public Ticker(int width) { 22 preWidth = -1; 23 msg = "Hello..."; 24 font = new Font("Helvetica", Font.BOLD, 36); 25 metrics = getFontMetrics(font); 26 textWidth = metrics.stringWidth(msg); 27 textHeight = metrics.getHeight(); 28 textDescent = metrics.getDescent(); 29 setSize(width, 50); 30 runner = new Thread(this); 31 runner.start(); 32 }
쓰레드 예: Ticker.java 35 public void stop() { 36 if(runner != null) { 37 stop = true; 38 runner=null; 39 } 40 } 41 42 public void run() { 43 while(!stop) { 44 try { 45 runner.sleep(50); 46 } catch(InterruptedException e) 47 repaint(); 48 } 49 } 50 51 public void update(Graphics g) { 52 paint(g); 53 }
쓰레드 예: TickerFrame.java 1 import java.awt.*; 2 import java.awt.event.*; 3 4 public class TickerFrame extends Frame { 5 6 TickerFrame() { 7 super("TickerFrame"); 8 Ticker ta = new Ticker(getSize().width); 9 add("North", ta); 10 add("Center", new Button("CENTER")); 11 addWindowListener(new WindowAdapter() { 12 public void windowClosing(WindowEvent e) { 13 setVisible(false); 14 dispose(); 15 System.exit(0); 16 } 17 }); 18 19 setSize(400, 200); ………..