10 초동안사용자가입력하지않으면종료하는예제 ) import javax.swing.joptionpane; class AutoTermination { static boolean inputcheck = false; public static void main(string[] args) throws Exception { FirstThread th1 = new FirstThread(); SecondThread th2 = new SecondThread(); th1.start(); th2.start(); class FirstThread extends Thread {// 입력값을받는일을하는스레드 System.out.println("10초안에값을입력해야합니다."); String input = JOptionPane.showInputDialog(" 아무값이나입력하세요."); AutoTermination.inputCheck = true; System.out.println(" 입력값은 " + input + " 입니다."); class SecondThread extends Thread {//10초카운트를하는스레드 for(int i=9; i >= 0; i--) { if(autotermination.inputcheck) break; System.out.println(i); try { sleep(1000);//1초동안대기 catch(interruptedexception e ) { System.out.println("10초동안값이입력되지않아종료합니다."); System.exit(0);// 모든프로그램종료 // 즉, 값이입력되면종료. 값이입력되지않아도 10초가지나면종료 - 1 -
스레드의우선순위 우선순위설정메소드 : void setpriority(int newpriority) newpriority 에설정할수있는등급 : 1( 가장낮은우선순위 ) 부터 10( 가장높은우선순위 ) 가장높은우선순위 : MAX_PRIORITY, 보통우선순위 : NORM_PRIORITY, 가장낮은우선순위 : MIN_PRIORITY public static final int MAX_PRIORITY = 10; public static final int NORM_PRIORITY = 5; public static final int MIN_PRIORITY = 1; 우선순위를읽는메소드 : int getpriority() 우선순위설정예 ) class MyThread extends Thread { public MyThreadString name) { super(name); System.out.println(getName()+ " 스레드 "); class ThreadPriorityTest { public static void main(string[] str) { MyThread obj1 = new MyThread(" 최고우선순위 "); MyThread obj2 = new MyThread(" 보통우선순위 "); MyThread obj3 = new MyThread(" 최저우선순위 "); obj1.setpriority(thread.max_priority); // 최고우선순위 obj2.setpriority(thread.norm_priority); // 보통우선순위 obj3.setpriority(thread.min_priority); // 최저우선순위 obj3.start(); obj2.start(); obj1.start(); - 2 -
우선순위설정예 ) class ThreadPriorityTest { public static void main(string args[]) { Thread1 th1 = new Thread1(); Thread2 th2 = new Thread2(); th2.setpriority(7);// 두번째스레드의우선순위를 7 등급으로설정 System.out.println("Priority of th1(-) : " + th1.getpriority() ); System.out.println("Priority of th2( ) : " + th2.getpriority() ); th1.start();// 스레드시작 th2.start();// 스레드시작 class Thread1 extends Thread { for(int i=0; i < 300; i++) { System.out.print("-"); for(int x=0; x < 10000000; x++); class Thread2 extends Thread { for(int i=0; i < 300; i++) { System.out.print(" "); for(int x=0; x < 10000000; x++); - 3 -
데몬스레드 (Daemon Thread) 데몬스레드는일반스레드의작업을돕는보조적인역할을수행하는스레드이다. 일반스레드가모두종료되면데몬 스레드는강제적으로종료된다. 더이상보조적인역할을수행할수없기때문이다. 보저적인역할을한다는점만제외하고는다른스레드와동일하다. 데몬스레드는무한반복으로실행하면서대기하고있다가특정조건이만족되면작업을수행하고다시대기하도록작성한다. 데몬스레드는일반스레드의작성방법과실행방법이동일하며다만스레드를생성한후실행하기전에 setdaemon(true) 를호출하기만하면된다. 또한, 데몬스레드에서또다른스레드를생성한다면이스레드도데몬스레드가된다. boolean isdaemon() : 데몬스레드인지를검사하는메소드. 데몬스레드이면 true 를반환한다. void setdaemon(boolean on) : 데몬스레드로설정하는메소드 - 4 -
3 초마다변수 autosave 의값을확인해서그값이 true 이면, 자동으로저장하는일을무한히반복하는스레드 ) class AutoSaveTest implements Runnable { static boolean autosave = false; public static void main(string[] args) {// 주스레드 Thread t = new Thread(new AutoSaveTest()); t.setdaemon(true);// 이부분이없으면보조스레드는종료되지않는다. t.start();// 데몬스레드시작 for(int i=1; i <= 20; i++) {//20초동안반복 try{ Thread.sleep(1000);//1초동안대기 catch(interruptedexception e) { System.out.println(i);//i 값을화면에출력, 1 부터 20까지 1초간격으로출력된다. if(i==5)// 처음에 5 초가되면 autosave 를 true 로설정해서 자동저장 되도록함 autosave = true; System.out.println(" 프로그램을종료합니다."); // 데몬스레드 ( 보조스레드 : 주스레드가종료되면강제적으로종료된다.) while(true) { try { Thread.sleep(3 * 1000); //3초동안대기 catch(interruptedexception e) { // autosave의값이 true이면 autosave() 를호출한다. if(autosave) { autosave(); //3초동안대기한후에다시 autusave값이 true인지를검사한다. public void autosave() { System.out.println(" 작업파일이자동저장되었습니다."); - 5 -
스레드의상태제어 스레드를실행시키면특별한조치를취하기전까지는스레드들은독립적으로실행되고종료된다. 그런데, 임의로스레 드들간의실행을조절할필요가있다. 이러한경우에몇가지메소드를사용해서스레드의실행상태를조절할수있 다. join() 메소드는이메소드를호출한스레드가일시정시상태가된다. 다음의예는 main 스레드가 th1 과 th2 스레드가모두종료될때까지기다렸다가나중에종료되는프로그램이다. class ThreadStateTest { static long starttime = 0; public static void main(string args[]) { Thread1 th1 = new Thread1(); Thread2 th2 = new Thread2(); th1.start(); th2.start(); starttime = System.currentTimeMillis(); try { th1.join();// th1 스레드의작업이끝날때까지기다린다. th2.join();// th2 스레드의작업이끝날때까지기다린다. catch(interruptedexception e) { System.out.print(" 소요시간 :" + (System.currentTimeMillis() - ThreadStateTest.startTime)); // main class Thread1 extends Thread { for(int i=0; i < 300; i++) { System.out.print("-"); // run() class Thread2 extends Thread { for(int i=0; i < 300; i++) { System.out.print(" "); // run - 6 -
첫번째스레드가모두종료된후에두번째스레드를실행시키는예 ) class ThreadStateTest2 { public static void main(string args[]) { Thread1 th1 = new Thread1(); Thread2 th2 = new Thread2(); th1.start();// 첫번째스레드시작 try { th1.join();// 첫번째스레드가완전히종료될때까지기다린다. catch(interruptedexception e) { th2.start();// 두번째스레드시작 class Thread1 extends Thread { for(int i=0; i < 300; i++) { System.out.print("-"); class Thread2 extends Thread { for(int i=0; i < 300; i++) { System.out.print(" "); - 7 -
interrupt() 메소드는잠자고있는 (sleep 상태에있는 ) 메소드를깨워서실행대기상태로변경시킨다. 다음은 interrupt() 메소드를이용해서잠자고있는데몬스레드를깨워서일을하도록하는예이다. 이프로그램은가비지컬렉션, 즉사용가능한메모리크기가줄어들면자동으로메모리청소작업을진행해서사용가능 한메모리크기를증가시키는일을한다. 가비지컬렉션은데몬스레드로설정한다. class ThreadInterruptTest { public static void main(string args[]) { GargabeCollection gc = new GargabeCollection();// 가비지컬렉션역할의스레드생성 gc.setdaemon(true);//gargabecollection 스레드를데몬스레드로설정 gc.start();// 가비지컬렉션스레드시작 int requiredmemory = 0;// 필요한메모리의크기. for(int i=0; i < 20; i++) { requiredmemory = (int)(math.random() * 10) * 20; // 필요한메모리의크기를난수를사용해서설정한다. // 즉, 컴퓨터에서동작하는프로그램에서사용하려는메모리의크기를흉내낸것이다. // 필요한메모리가사용할수있는양보다크거나전체메모리의 60% 이상을 // 사용했을경우 gc를깨운다. if(gc.freememory() < requiredmemory gc.freememory() < gc.totalmemory() * 0.4) { gc.interrupt();// 잠자고있는데몬스레드를깨운다. gc.usedmemory += requiredmemory; // 사용중인메모리크기가프로그램에서사용하는메모리크기만큼증가한다. System.out.println("usedMemory:"+gc.usedMemory);// 사용중인메모리크기를출력 - 8 -
class GargageCollection extends Thread { final static int MAX_MEMORY = 1000;// 메모리의최대크기, 즉메모리크기가 1000 이상이될수없슴 int usedmemory = 0;// 사용중인메모리크기 while(true) { try { Thread.sleep(10 * 1000);//10초를자고일어나서가비지컬렉션을수행한다. catch(interruptedexception e) { System.out.println("Awaken by interrupt()."); //gc.interrupt(); 에의해서스레드가잠에서깰때이예외가발생한다. gc();//10초동안자고일어나서 garbage collection을수행한다. System.out.println("Garbage Collected. Free Memory :" + freememory()); // 가비지컬렉션을수행한후의메모리크기를출력한다. public void gc() {// 가비지컬렉션을수행하는메소드 usedmemory -= 300;// 사용중인메모리크기를줄인다. if(usedmemory < 0) usedmemory = 0;// 사용중인메모리값이마이너스가되지않도록한다. public int totalmemory() {// 전체메모리크기를알려주는메소드 return MAX_MEMORY; public int freememory() {// 사용가능한메모리크기를알려주는메소드 return MAX_MEMORY - usedmemory; 위의스레드프로그램은메모리의최대크기가 1000으로설정되었기때문에, 어떠한경우에도출력된메모리의크기가 1000을넘을수없다. 그러나, 반복해서실행하다보면사용중인메모리의크기가 1000을넘어가는현상을발견할수있다. 무슨이유때문인지생각해본다. - 9 -
우선, 프로그램의실행과정을살펴본다. 메인스레드 1. 사용중인메모리의크기를점점증가시킨다. 2. 사용중인메모리의크기가일정수준을넘어서면, 데몬스레드를깨운다. ( 데몬스레드가자고있으면, 깨어나고, 깨어있으면아무일도일어나지않는다.) 3. 다시 1의작업을진행한다. 데몬스레드 1. 10초동안잠잔다. ( 메인스레드가 interrupt() 메소드를사용 해서깨우면 2 의작업을수행한다.) 2. 깬다. 3. 깨어나서가비지컬렉션을수행한다. 4. 다시 1 의작업을진행한다. 위의표와같이메인스레드와데몬스레드가독자적으로작업을진행한다. 이유는다음과같다. 메인스레드가 2단계에서데몬스레드를깨운후, 데몬스레드가 3단계작업을할때까지기다리지않고, 곧장 1단계작업을수행하기때문이다. 왜냐하면, 스레드들은서로독자적으로실행되기때문이다. 따라서, 메모리의크기를줄이는작업을시작하기바로직전에메모리크기를계속해서증가시키는작업을하기때문이다. 그러면, 어떻게해야이문제를해결할수있을까? 메인스레드가데몬스레드에게시간을강제적으로줘서가비지컬렉션작업을할시작전인여유를보장하는것이다. 즉, 다음과같이잠자고있는데몬스레드를깨우는일만하는것이아니라, gc.interrupt();// 잠자고있는데몬스레드를깨운다. 깨우고나서일정한시간을기다려서가비지컬렉션작업을할수있도록작업시간을보장해준다. gc.interrupt();// 잠자고있는데몬스레드를깨운다. try { gc.join(100);// 가비지컬렉션이 0.1초동안일을할동안기다린다. catch (InterruptedException e) { 문제해결!!! - 10 -
스레드의동기화 다음예에서스레드클래스의지역변수와인스턴스변수값의변화를살펴본다. class ThreadVariableTest { public static void main(string args[]) { RunnableImpl r = new RunnableImpl(); Thread t1 = new Thread(r); Thread t2 = new Thread(r); t1.start(); t2.start(); class RunnableImpl implements Runnable { int iv = 0;// 인스턴스변수 int lv =0;// 지역변수 String name = Thread.currentThread().getName();// 스레드의이름 while(lv < 3) { System.out.println(name + " 지역변수 : " + ++lv);// 지역변수값을 1증가 System.out.println(name + " 인스턴스변수 : " + ++iv);// 인스턴스변수값을 1증가 System.out.println(); // run() 이프로그램의결과를꼭확인한다!!! 하나의인스턴스 (RunnableImpl) 를두개의스레드가공유하기때문에인스턴스변수 iv 값은두스레드에서모두 접근이가능하다. 따라서, 계속해서증가를한다. 지역변수는공유되지않기때문에즉, 콜스텍에생성되므로공유되 지않는다, 지역변수값은스레드에영향을받지않는다. RunnableImpl r = new RunnableImpl();// 딱한번인스턴스를생성했으므로, 인스턴스변수도딱하나만생성된다. Thread t1 = new Thread(r);//r 을공유하고있다. Thread t2 = new Thread(r);//r 을공유하고있다. - 11 -
그런데, 다음의경우는인스턴스가두개이므로인스턴스변수도두개가생성된다. class ThreadVariableTest { public static void main(string args[]) { RunnableImpl r1 = new RunnableImpl();// 첫번째인스턴스생성 RunnableImpl r2 = new RunnableImpl();// 두번째인스턴스생성 Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start(); 이전프로그램과위프로그램의결과를비교해본다!!! - 12 -
스레드를이용한은행의 ATM 을이용한입출금예 ) 이프로그램은서로다른두명의사용자가하나의계좌에출금을시도하는내용이다. public class Bank_of_Thread { public static void main(string[] args) { Runnable r = new RunnableEx24(); Thread t1 = new Thread(r);// 첫번째스레드, 즉첫번째사용자에해당한다. Thread t2 = new Thread(r);// 두번째스레드, 즉두번째사용자에해당한다. t1.start();// 출금시도 t2.start();// 출금시도 class Account { int balance = 1000;// 현계좌에잔고가 1000 원부터시작 public void withdraw(int money){ if(balance >= money) {// 잔고가출금하려는금액보다크면출금 try { Thread.sleep(1000); catch(exception e) { balance -= money; // withdraw의끝 class RunnableEx24 implements Runnable { Account acc = new Account(); while(acc.balance > 0) { // 100, 200, 300중의한값을임으로선택해서출금 (withdraw) int money = (int)(math.random() * 3 + 1) * 100; acc.withdraw(money);// 지정된금액을출금 System.out.println("balance:"+acc.balance);// 출금후잔고출력 // run() 의끝 위의프로그램을여러번실행시켜서잔고를확인한다. 잔고에마이너스상태가발생하는가? 발생하면왜발생하는지생각해본다. - 13 -