멀티태스킹과스레드개념 Thread & Multitasking 멀티태스킹 (Multi-tasking) 하나의응용프로그램이여러개의작업 ( 태스크 ) 을동시에처리 스레드 (Thread) 마치바늘이하나의실 (thread) 을가지고바느질하는것과자바의스레드는일맥상통함 514770-1 2017 년봄학기 5/10/2017 박경신 다림질하면서이어폰으로전화하는주부운전하면서화장하는운전자제품의판독과포장작업의두기능을갖춘기계 Process vs Thread 프로세스 (process) 운영체제로부터 process 를할당받고, 운영되기위해필요한주소공간, 메모리등자원을할당받는다. 스레드 (thread) 한프로세스내에서동작되는여러실행의흐름으로, 같은 process 내의주소공간이나자원들을 thread 끼리공유하면서실행된다. Single vs Multithreaded Processes Threads are light-weight processes within a process UNIX PROCESS THREADS WITHIN A UNIX PROCESS https://computing.llnl.gov/tutorials/pthreads/ https://www.cs.uic.edu/~jbell/coursenotes/operatingsystems/4_threads.html
스레드와멀티스레딩 웹서버의멀티스레딩사례 스레드 프로그램코드를이동하면서실행하는하나의제어 자바의멀티태스킹 멀티스레딩 (Multi-threading) 만가능 자바에프로세스개념은존재하지않고, 스레드개념만존재 스레드는실행단위 스레드는스케쥴링단위 하나의응용프로그램은여러개의스레드로구성가능 스레드사이의통신오버헤드가크지않음 각클라이언트당웹서비스스레드생성 웹서버 웹서비스스레드 웹서비스스레드 웹서비스스레드 웹문서요청웹문서전송웹문서요청웹문서전송웹문서요청웹문서전송 웹클라이언트 웹클라이언트 웹클라이언트 웹서비스스레드 웹문서전송 웹문서요청 웹클라이언트 웹서버시스템 자바스레드 (Thread) 란? JVM 과자바응용프로그램, 스레드의관계 자바스레드 자바가상기계 (JVM) 에의해스케쥴되는실행단위의코드블럭 스레드의생명주기는 JVM 에의해관리됨 JVM 은스레드단위로스케쥴링 JVM 과멀티스레드의관계 하나의 JVM 은하나의자바응용프로그램만실행 자바응용프로그램이시작될때 JVM 이함께실행됨 자바응용프로그램이종료하면 JVM 도함께종료함 하나의응용프로그램은하나이상의스레드로구성가능 두개의자바응용프로그램이동시에실행시키고자하면두개의 JVM 을이용하고응용프로그램은서로소켓등을이용하여통신
자바스레드와 JVM 스레드만들기 각스레드의스레드코드는응용프로그램내에존재함 JVM 이스레드를관리함 스레드가몇개인지? 스레드코드의위치가어디인지? 스레드의우선순위는얼마인지? 등 스레드실행을위해개발자가하는작업 스레드코드작성 스레드를생성하고스레드코드를실행하도록 JVM에게요청 스레드만드는 2 가지방법 java.lang.thread 클래스를상속하는경우 java.lang.runnable 인터페이스를구현하는경우 현재하나의 JVM 에의해 4 개의스레드가실행중이며그중스레드 2 가 JVM 에의해스케쥴링되어실행되고있음 Thread 클래스의메소드 생성자 Thread() Thread(Runnable target) Thread(String name) Thread(Runnable target, String name) 스레드시작시키기 void start() 스레드코드 void run() 스레드잠자기 static void sleep(long mills) 다른스레드죽이기 void interrupt() Thread 클래스의메소드 다른스레드에게양보 static void yield() 현재스레드실행을중단하고다른스레드가실행될수있게양보 다른스레드가죽을때까지기다리기 void join() 현재스레드객체알아내기 static Thread currentthread() 스레드 ID 알아내기 long getid() 스레드이름알아내기 String getname() 스레드우선순위값알아내기 int getpriority() 스레드의상태알아내기 Thread.State getstate()
Thread 클래스를이용한스레드생성 Thread 를상속받아 1 초단위로출력하는 TimerThread 클래스 스레드클래스작성 Thread 클래스상속새클래스작성 스레드코드작성 run() 메소드오버라이딩 run() 메소드를스레드코드라고부름 run() 메소드에서스레드실행시작 스레드객체생성 스레드시작 start() 메소드호출 스레드로작동시작 JVM 에의해스케쥴되기시작함... // run() 오버라이딩... TimerThread th = new TimerThread(); 스레드클래스정의 스레드코드작성 1 초에한번씩 n 을증가시켜콘솔에출력한다. 스레드객체생성 스레드시작 JVM main() 스레드 th main 스레드의스레드정보 int n = 0; // 무한루프를실행한다. System.out.println(n); sleep(1000); //1초동안잠을잔후깨어난다. catch(interruptedexception e){return; public class TestThread { TimerThread th = new TimerThread(); TimerThread 스레드 run() {... TimerThread 의스레드정보 0 1 2 3 4 Thread 를상속받아 1 초단위로출력하는타이머레이블만들기 스레드주의사항 import java.awt.*; import javax.swing.*; JLabel timerlabel; public TimerThread(JLabel timerlabel) { this.timerlabel = timerlabel; int n=0; timerlabel.settext(integer.tostring(n)); Thread.sleep(1000); catch(interruptedexception e) { return; public class ThreadTimerEx extends JFrame { public ThreadTimerEx() { settitle("threadtimerex 예제 "); setdefaultcloseoperation(jframe.exit_on_close); Container c = getcontentpane(); c.setlayout(new FlowLayout()); JLabel timerlabel = new JLabel(); timerlabel.setfont(new Font("Gothic", Font.ITALIC, 80)); TimerThread th = new TimerThread(timerLabel); c.add(timerlabel); setsize(300,150); setvisible(true); public static void main(string[] args) { new ThreadTimerEx(); run() 메소드가종료하면스레드는종료한다. 스레드가계속살아있도록하려면 run() 메소드내무한루프구성 한번종료한스레드는다시시작시킬수없다. 다시스레드객체를생성하고스레드로등록하여야한다. 한스레드에서다른스레드를강제종료할수있다.
Runnable 인터페이스로스레드만들기 스레드클래스작성 Runnable 인터페이스구현하는새클래스작성 스레드코드작성 run() 메소드구현 run() 메소드를스레드코드라고부름 run() 메소드에서스레드실행시작 Thread th = new Thread(new TimerRunnable()); 스레드객체생성 스레드시작 start() 메소드호출 class TimerRunnable implements Runnable {... // run() 메소드구현... Runnable인터페이스를구현하여 1초단위출력 TimerRunnable 클래스 Runnable 클래스로구현 스레드코드작성 1 초에한번씩 n 을증가시켜콘솔에출력한다. 스레드객체생성 스레드시작 JVM main() 스레드 th main 스레드의스레드정보 class TimerRunnable implements Runnable { int n = 0; // 무한루프를실행한다. System.out.println(n); Thread.sleep(1000); // 1초동안잠을잔후깨어난다. catch(interruptedexception e) { return; public class TestRunnable { Thread th = new Thread(new TimerRunnable()); Thread 스레드 run() {... TimerThread 의스레드정보 0 1 2 3 4 Runnable 인터페이스를구현하여 1 초단위출력레이블만들기 스레드정보 import java.awt.*; import javax.swing.*; class TimerRunnable implements Runnable { JLabel timerlabel; public TimerRunnable(JLabel timerlabel) { this.timerlabel = timerlabel; int n=0; timerlabel.settext(integer.tostring(n)); Thread.sleep(1000); catch(interruptedexception e) { return; public class RunnableTimerEx extends JFrame { public RunnableTimerEx() { settitle("runnabletimerex 예제 "); setdefaultcloseoperation(jframe.exit_on_close); Container c = getcontentpane(); c.setlayout(new FlowLayout()); JLabel timerlabel = new JLabel(); timerlabel.setfont(new Font("Gothic", Font.ITALIC, 80)); TimerRunnable runnable = new TimerRunnable(timerLabel); Thread th = new Thread(runnable); c.add(timerlabel); setsize(300,150); setvisible(true); public static void main(string[] args) { new RunnableTimerEx();
스레드상태 스레드상태 6 가지 NEW 스레드가생성되었지만스레드가아직실행할준비가되지않았음 RUNNABLE 스레드가 JVM 에의해실행되고있거나실행준비되어스케쥴링을기다리는상태 WAITING 다른스레드가 notify(), notifyall() 을불러주기를기다리고있는상태 스레드동기화를위해사용 TIMED_WAITING 스레드가 sleep(n) 을호출하여 n 밀리초동안잠을자고있는상태 BLOCK 스레드가 I/O 작업을요청하면 JVM 이자동으로이스레드를 BLOCK 상태로만듦 TERMINATED 스레드가종료한상태 스레드상태는 JVM 에의해기록관리됨 스레드상태와생명주기 스레드상태 6 가지 NEW RUNNABLE WAITING TIMED_WAITING BLOCK TERMINATED ** wait(), notify(), notifyall() 은 Thread 의메소드가아니며 Object 의메소드임 스레드우선순위와스케쥴링 스레드의우선순위 (Priority) 최대값 = 10(MAX_PRIORITY) 최소값 = 1(MIN_PRIORITY) 보통값 = 5(NORMAL_PRIORITY) 스레드우선순위는응용프로그램에서변경가능 void setpriority(int priority) int getpriority() main() 스레드의우선순위값은초기에 5 스레드는부모스레드와동일한우선순위값을가지고탄생 JVM 의스케쥴링정책 철저한우선순위기반 가장높은우선순위의스레드가우선적으로스케쥴링 동일한우선순위의스레드는돌아가면서스케쥴링 ( 라운드로빈 ) main() 은자바의 main 스레드 main 스레드와 main() 메소드 JVM 은응용프로그램이실행될때스레드생성 : main 스레드 main 스레드에의해 main() 메소드실행 main() 메소드가종료하면 main 스레드종료 public class ThreadMainEx { long id = Thread.currentThread().getId(); String name = Thread.currentThread().getName(); int priority = Thread.currentThread().getPriority(); Thread.State s = Thread.currentThread().getState(); System.out.println(" 현재스레드이름 = " + name); System.out.println(" 현재스레드 ID = " + id); System.out.println(" 현재스레드우선순위값 = " + priority); System.out.println(" 현재스레드상태 = " + s); 현재스레드이름 = main 현재스레드 ID = 1 현재스레드우선순위값 = 5 현재스레드상태 = RUNNABLE
스레드종료와타스레드강제종료 스스로종료 run() 메소드리턴 타스레드에서강제종료 : interrupt() 메소드사용 TimerThread th = new TimerThread(); th.interrupt(); // 강제종료 main() 스레드 th th.interrupt() ; InterruptedException 발생 int n = 0; System.out.println(n); // 화면에카운트값출력 sleep(1000); 만일 return 하지않으면 catch(interruptedexception e){ 스레드는종료하지않음 return; // 예외를받고스스로리턴하여종료 TimerThread 스레드 catch(interruptedexception e) {return; main 스레드의 interrupt() 메소드호출에의해 catch 문실행. 그리고종료 1 초씩작동하는타이머스레드강제종료 import java.awt.*; import java.awt.event.*; import javax.swing.*; class TimerRunnable implements Runnable { JLabel timerlabel; public TimerRunnable(JLabel timerlabel) { this.timerlabel = timerlabel; int n=0; timerlabel.settext(integer.tostring(n)); Thread.sleep(1000); // 1 초 sleep catch(interruptedexception e) { return; // 예외발생시스레드종료 정상작동 버튼을클릭하면타이머가멈춘다. 버튼은비활성화된다. public class ThreadInterruptEx extends JFrame { Thread th; public ThreadInterruptEx() { settitle(" hreadinterruptex 예제 "); setdefaultcloseoperation(jframe.exit_on_close); Container c = getcontentpane(); c.setlayout(new FlowLayout()); JLabel timerlabel = new JLabel(); timerlabel.setfont(new Font("Gothic", Font.ITALIC, 80)); TimerRunnable runnable = new TimerRunnable(timerLabel); th = new Thread(runnable); // 스레드생성 c.add(timerlabel); // 버튼을생성하고 Action 리스너등록 JButton btn =new JButton("kill Timer"); btn.addactionlistener(new ActionListener() { public void actionperformed(actionevent e) { th.interrupt(); // 타이머스레드강제종료 JButton btn = (JButton)e.getSource(); btn.setenabled(false); // 버튼비활성화 ); c.add(btn); setsize(300,150); setvisible(true); // 스레드동작시킴 public static void main(string[] args) { new ThreadInterruptEx(); flag 를이용한종료 스레드동기화 (Thread Synchronization) 스레드 A 가스레드 B 의 flag 를 true 로만들면, 스레드 B 가스스로종료하는방식 TimerThread th = new TimerThread(); th.finish(); // TimerThread 강제종료 main() 스레드 th th.finish(); flag 멤버를 true 로변경 int n = 0; bool flag = false; // false로초기화 public void finish() { flag = true; System.out.println(n); // 화면출력 sleep(1000); if(flag == true) return; // 스레드종료 catch(interruptedexception e){ return; TimerThread 스레드 flag false true if(flag == true) return; // 스레드종료 멀티스레드프로그램작성시주의점 다수의스레드가공유데이터에동시에접근하는경우 공유데이터의값에예상치못한결과발생가능 스레드동기화 멀티스레드의공유데이터의동시접근문제해결책 공유데이터를접근하는모든스레드의한줄세우기 한스레드가공유데이터에대한작업을끝낼때까지다른스레드가대기하도록함
두스레드의프린터동시쓰기로충돌하는경우 공유집계판에동시접근하는경우 스레드 A 스레드 B 스레드 B 자바는좋은것이야. 배워서많이알고취직도잘되고. 스레드 A 가프린터사용을끝낼때까지기다린다. 50+10 =60 50+10 =60 학생 B 는학생 A 가계수를끝낸후계수를시작함 학생 A 는 60 을기록하고나감 I love you forever. 자바는좋은것이야. 배워서많이알고취직도잘되고. 스레드 A I love you forever. 학생 A 학생 C I love 자바는좋은것이야. you 배워서많이 forever. 알고취직도잘되고. 두스레드가동시에프린터에쓰는경우문제발생 I love you forever. 한스레드의출력이끝날때까지대기함으로써정상출력 학생 A 학생 B 두학생이동시에방에들어와서집계판을수정하는경우집계판의결과가잘못됨 학생 B 방에먼저들어간학생이집계를끝내기를기다리면정상처리 synchronized 블록지정 synchronized 키워드 한스레드가독점적으로실행해야하는부분 ( 동기화코드 ) 을표시하는키워드 - 임계영역 (critical section) 표기키워드 synchronized 사용 메소드전체혹은코드블록 synchronized 블록이실행될때, 먼저실행한스레드가모니터소유 모니터란해당객체를독점적으로사용할수있는권한 모니터를소유한스레드가모니터를내놓을때까지다른스레드대기 void execute() { // 다른코드들 synchronized void add() { synchronized(this) { int n = getcurrentsum(); int n = getcurrentsum(); n+=10; n+=10; setcurrentsum(n); setcurrentsum(n); synchronized 메소드 // 다른코드들 synchronized 사용예 : 집계판 public class SynchronizedEx { SyncObject obj = new SyncObject(); Thread th1 = new WorkerThread("kitae", obj); Thread th2 = new WorkerThread("hyosoo", obj); th1.start(); th2.start(); class SyncObject { int sum = 0; synchronized void add() { int n = sum; Thread.yield(); // 현재실행하고있는스레드가다른스레드에게양보 n += 10; sum = n; System.out.println(Thread.currentThread().getName() + " : " + sum); int getsum() {return sum; class WorkerThread extends Thread { SyncObject sobj; WorkerThread(String name, SyncObject sobj) { super(name); this.sobj = sobj; int i=0; while(i<10) { sobj.add(); i++; 집계판 : class SyncObject 각학생 : class WorkerThread kitae : 10 hyosoo : 20 kitae : 30 hyosoo : 40 kitae : 50 hyosoo : 60 kitae : 70 hyosoo : 80 hyosoo : 90 hyosoo : 100 hyosoo : 110 hyosoo : 120 hyosoo : 130 hyosoo : 140 kitae : 150 kitae : 160 kitae : 170 kitae : 180 kitae : 190 kitae : 200 kitae 와 hyosoo 가각각 10 번씩 add() 를호출하였으며동기화가잘이루어져서최종누적점수 sum 이 200 이됨
SyncObject 에대한두스레드의동시접근과정 집계판에서 synchronized 사용하지않을경우 public class SynchronizedEx { SyncObject obj = new SyncObject(); Thread th1 = new WorkerThread("kitae", obj); Thread th2 = new WorkerThread("hyosoo", obj); th1.start(); th2.start(); class SyncObject { int sum = 0; void add() { // synchronized 가없을경우 int n = sum; Thread.yield(); n += 10; sum = n; System.out.println(Thread.currntThread().getName() + " : " + sum); int getsum() {return sum; class WorkerThread extends Thread { SyncObject sobj; WorkerThread(String name, SyncObject sobj) { super(name); this.sobj = sobj; int i=0; while(i<10) { sobj.add(); i++; kitae : 10 hyosoo : 20 kitae : 30 hyosoo : 30 kitae : 40 hyosoo : 50 kitae : 50 kitae : 60 hyosoo : 60 kitae : 70 hyosoo : 70 kitae : 80 hyosoo : 90 kitae : 100 hyosoo : 100 kitae : 110 hyosoo : 120 kitae : 130 hyosoo : 140 hyosoo : 150 kitae 와 hyosoo 가각각 10 번씩 add() 를호출하였지만동기화가이루어지지않아공유변수 sum 에대한접근에충돌이있었고, 수를많이잃어버리게되어누적점수가 150 밖에되지못함 wait(), notify(), notifyall() 동기화객체 두개이상의스레드사이에동기화작업에사용되는객체 동기화메소드 synchronized 블록내에서만사용되어야함 wait() 다른스레드가 notify() 를불러줄때까지기다린다. notify() wait() 를호출하여대기중인스레드를깨우고 RUNNABLE 상태로만든다. 2 개이상의스레드가대기중이라도오직한스레드만깨운다. notifyall() wait() 를호출하여대기중인모든스레드를깨우고모두 RUNNABLE 상태로만든다. wait(), notify(), notifyall() 은 Object 의메소드 모든객체가동기화객체가될수있다. Thread 객체도동기화객체로사용될수있다. Thread A 가 ObjectS.wait() 를호출하여무한대기하고, Thread B 가 ObjectS.notify() 를호출하여 ObjectS 에대기하고있는 Thread A 를깨운다. 4 개의스레드가모두 ObjectS.wait() 를호출하여대기하고, ThreadA 는 ObjectS.notify() 를호출하여대기중인스레드중하나만깨우는경우 4 개의스레드가모두 ObjectS.wait() 를호출하여대기하고, ThreadA 는 ObjectS.notifyAll() 를호출하여대기중인 4 개의스레드를모두깨우는경우 36 잠자는 4 개의스레드중하나만깨운다. 선택받지못한 3 개의스레드는계속잠을잔다.