Multi-tasking vs Thread Thread & Multitasking 멀티태스킹 (Multi-tasking) 하나의응용프로그램이여러개의작업 ( 태스크 ) 을동시에처리 스레드 (Thread) 마치바늘이하나의실 (thread) 을가지고바느질하는것과자바의스레드는일맥상통함 514770 2018 년가을학기 11/19/2018 박경신 다림질하면서이어폰으로전화하는주부운전하면서화장하는운전자제품의판독과포장작업의두기능을갖춘기계 Process vs Thread 프로세스 (process) 운영체제로부터 process를할당받고, 운영되기위해필요한주소공간, 메모리등자원을할당받는다. 스레드 (thread) 한프로세스내에서동작되는여러실행의흐름으로, 같은 process 내의주소공간이나자원들을 thread끼리공유하면서실행된다. Why Thread? 프로세스 (process) 프로세스를생성하거나파괴하는데비용이많이듬 더많은메모리공간이필요함 프로세스복원또는교환을위한비용이많이듬 메모리맵변경이필요함 스레드 (thread) 스레드풀 (a pool of threads) 을유지하고다시사용 (reuse) 할수있슴 메모리공간이공유되며항상스왑될필요는없슴 프로세스의모든자원을공유함 UNIX PROCESS THREADS WITHIN A UNIX PROCESS https://computing.llnl.gov/tutorials/pthreads/
Single vs Multithreaded Process Threaded Application Threads are light-weight processes within a process 각클라이언트당웹서비스스레드생성 웹서비스스레드 웹서비스스레드 웹문서요청웹문서전송웹문서요청웹문서전송 웹클라이언트 웹클라이언트 웹서버 웹서비스스레드 웹문서전송 웹문서요청 웹클라이언트 웹서비스스레드 웹문서전송 웹문서요청 웹클라이언트 웹서버시스템 https://www.cs.uic.edu/~jbell/coursenotes/operatingsystems/4_threads.html Java Thread 스레드 프로그램코드를이동하면서실행하는하나의제어 자바의멀티태스킹 멀티스레딩 (Multi-threading) 만가능 자바에프로세스개념은존재하지않고, 스레드개념만존재 스레드는실행단위 스레드는스케쥴링단위 하나의자바응용프로그램 (instance of JVM) 은여러개의스레드로구성가능 스레드사이의통신오버헤드가크지않음 Java Thread 자바스레드 자바가상기계 (JVM) 에의해스케쥴되는실행단위의코드블럭 스레드의생명주기는 JVM 에의해관리됨 JVM 은스레드단위로스케쥴링 JVM과멀티스레드의관계 하나의 JVM은하나의자바응용프로그램만실행 자바응용프로그램이시작될때 JVM이함께실행됨 자바응용프로그램이종료하면 JVM도함께종료함 하나의응용프로그램은하나이상의스레드로구성가능
Java Application JVM & Thread Java Application JVM & Thread 각스레드의스레드코드는응용프로그램내에존재함 JVM 이스레드를관리함 스레드가몇개인지? 스레드코드의위치가어디인지? 스레드의우선순위는얼마인지? 등 두개의자바응용프로그램이동시에실행시키고자하면두개의 JVM 을이용하고응용프로그램은서로소켓등을이용하여통신 현재하나의 JVM 에의해 4 개의스레드가실행중이며그중스레드 2 가 JVM 에의해스케쥴링되어실행되고있음 Create Thread in Java 스레드실행을위해개발자가하는작업 스레드코드작성 스레드를생성하고스레드코드를실행하도록 JVM에게요청 스레드만드는 2 가지방법 java.lang.thread 클래스를상속하는경우 java.lang.runnable 인터페이스를구현하는경우 Thread Runnable Thread MyThread MyClass (objects are threads) (objects with run() body) Thread Class Method 생성자 Thread() Thread(Runnable target) Thread(String name) Thread(Runnable target, String name) 스레드시작시키기 void start() 스레드코드 void run() 스레드잠자기 static void sleep(long mills) 다른스레드죽이기 void interrupt()
Thread Class Method 다른스레드에게양보 static void yield() 현재스레드실행을중단하고다른스레드가실행될수있게양보 다른스레드가죽을때까지기다리기 void join() 현재스레드객체알아내기 static Thread currentthread() 스레드 ID 알아내기 long getid() 스레드이름알아내기 String getname() 스레드우선순위값알아내기 int getpriority() 스레드의상태알아내기 Thread.State getstate() Extends Thread 스레드클래스작성 Thread 클래스상속새클래스작성 스레드코드작성 run() 메소드오버라이딩 스레드객체생성 스레드시작 start() 메소드호출 스레드로작동시작 JVM에의해스케쥴되기시작함 class TimerThread extends Thread {...... // thread body of execution TimerThread th = new TimerThread(); 1 초단위로출력하는 TimerThread 클래스 1 초단위로출력하는타이머레이블만들기 스레드클래스정의 스레드코드작성 1 초에한번씩 n 을증가시켜콘솔에출력한다. 스레드객체생성 스레드시작 JVM main() 스레드 th main 스레드의스레드정보 class TimerThread extends Thread { int n = 0; while(true) { // 무한루프를실행한다. 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 import java.awt.*; import javax.swing.*; class TimerThread extends Thread { JLabel timerlabel; public TimerThread(JLabel timerlabel) { this.timerlabel = timerlabel; int n=0; while(true) { 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() 메소드내무한루프구성 한번종료한스레드는다시시작시킬수없다. 다시스레드객체를생성하고스레드로등록하여야한다. 한스레드에서다른스레드를강제종료할수있다. Implements Runnable 스레드클래스작성 Runnable 인터페이스구현하는새클래스작성 스레드코드작성 run() 메소드구현 스레드객체생성 class TimerRunnable implements Runnable {...... // thread body of execution Thread th = new Thread(new TimerRunnable()); Thread 객체를생성하고 Runnable 객체를매개변수로넘김 스레드시작 start() 메소드호출 Runnable 클래스로구현스레드코드작성 1초에한번씩 n을증가시켜콘솔에출력한다. 스레드객체생성스레드시작 JVM 1 초단위로출력하는 TimerRunnable 클래스 main() 스레드 th main 스레드의스레드정보 class TimerRunnable implements Runnable { int n = 0; while(true) { // 무한루프를실행한다. System.out.println(n); Thread.sleep(1000); // 1초동안잠을잔후깨어난다. catch(interruptedexception e) { return; public class TestRunnable { Thread th = new Thread(new TimerRunnable()); Thread 스레드 run() {... TimerRunnable 의스레드정보 0 1 2 3 4 1 초단위로출력하는타이머레이블만들기 import java.awt.*; import javax.swing.*; class TimerRunnable implements Runnable { JLabel timerlabel; public TimerRunnable(JLabel timerlabel) { this.timerlabel = timerlabel; int n=0; while(true) { 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();
스레드정보 Thread State 스레드상태 6 가지 NEW 스레드가생성되었지만스레드가아직실행할준비가되지않았음 RUNNABLE 스레드가 JVM 에의해실행되고있거나실행준비되어스케쥴링을기다리는상태 WAITING 다른스레드가 notify(), notifyall() 을불러주기를기다리고있는상태 스레드동기화를위해사용 TIMED_WAITING 스레드가 sleep(n) 을호출하여 n 밀리초동안잠을자고있는상태 BLOCK 스레드가 I/O 작업을요청하면 JVM 이자동으로이스레드를 BLOCK 상태로만듦 TERMINATED 스레드가종료한상태 스레드상태는 JVM 에의해기록관리됨 Thread State and Life-Cycle 스레드상태 6 가지 NEW RUNNABLE WAITING TIMED_WAITING BLOCK TERMINATED ** wait(), notify(), notifyall() 은 Thread 의메소드가아니며 Object 의메소드임 Thread Priority 스레드의우선순위 (Priority) 최대값 = 10(MAX_PRIORITY) 최소값 = 1(MIN_PRIORITY) 보통값 = 5(NORMAL_PRIORITY) 스레드우선순위는응용프로그램에서변경가능 void setpriority(int priority) int getpriority() main() 스레드의우선순위값은초기에 5 스레드는부모스레드와동일한우선순위값을가지고탄생 JVM 의스케쥴링정책 철저한우선순위기반 가장높은우선순위의스레드가우선적으로스케쥴링 동일한우선순위의스레드는돌아가면서스케쥴링 ( 라운드로빈 )
Thread Scheduling 선점형 (Preemptive) 스케쥴링 더높은우선순위를가지는 Thread가 ready 상태가들어오면현재수행되던 Thread는강제로 ready 상태로가고우선순위가높은새로운 Thread가 ( 가능하면 ) 먼저수행되는방식 Thread.sleep(long millisecond) 현재쓰레드의실행을지정된시간 (ms) 동안중단하여 sleep 상태에들어가게함으로써 CPU 자원을양보하는효과적인수단 public class ThreadMainEx { MyThread my1 = new MyThread(); // MyThread extends Thread MyThread my2 = new MyThread(); MyThread my3 = new MyThread(); my1.setpriority(thread.min_priority); //my1.setpriority(1); my2.setpriority(thread.norm_priority); // my1.setpriority(5); my3.setpriority(thread.max_priority); // my1.setpriority(10); 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 발생 class TimerThread extends Thread { int n = 0; while(true) { System.out.println(n); // 화면에카운트값출력 sleep(1000); catch(interruptedexception e){ return; // 예외를받고스스로리턴하여종료 TimerThread 스레드 만일 return 하지않으면스레드는종료하지않음 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; while(true) { 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 를이용한종료 스레드 A 가스레드 B 의 flag 를 true 로만들면, 스레드 B 가스스로종료하는방식 TimerThread th = new TimerThread(); th.finish(); // TimerThread 강제종료 main() 스레드 th th.finish(); flag 멤버를 true 로변경 class TimerThread extends Thread { int n = 0; bool flag = false; // false로초기화 public void finish() { flag = true; while(true) { System.out.println(n); // 화면출력 sleep(1000); if(flag == true) return; // 스레드종료 catch(interruptedexception e){ return; TimerThread 스레드 flag false true if(flag == true) return; // 스레드종료 Thread Synchronization 멀티스레드프로그램작성시주의점 다수의스레드가공유데이터에동시에접근하는경우 공유데이터의값에예상치못한결과발생가능 스레드동기화 (Thread Synchronization) 멀티스레드의공유데이터의동시접근문제해결책 공유데이터를접근하는모든스레드의한줄세우기 한스레드가공유데이터에대한작업을끝낼때까지다른스레드가대기하도록함 두스레드의프린터동시쓰기로충돌하는경우 공유집계판에동시접근하는경우 스레드 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 add() 충돌 add() 충돌 add() 충돌 add() 충돌 add() 충돌 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 개의스레드를모두깨우는경우 38 잠자는 4 개의스레드중하나만깨운다. 선택받지못한 3 개의스레드는계속잠을잔다.