-
자바 - thread(3) 실행제어 메서드 예제언어/JAVA 2023. 3. 21. 15:42
sleep(long millis) :일정시간동안 쓰레드를 멈추게한다.
-Thread 클래스의 static 메서드로 항상 현재 쓰레드에만 적용된다. (sleep을 사용한 쓰레드)
- sleep()에 의해 일시정지가 상태가 된 쓰레드는 지정된 시간이 다 지나거나 interrupt()가 호출되면(InterruptedException이 발생) 실행대기 상태가 된다. (항상 try-catch필요)
주로 sleep을 포함하는 메서드를 만들어서 사용한다.
void delay(long millis){
//sleep()에 대한 try-catch
}
public class ThreadEX1 { public static void main(String[] args) throws Exception{ ThreadEx2 th1 = new ThreadEx2(); ThreadEx3 th2 = new ThreadEx3(); th1.start(); th2.start(); try{ Thread.sleep(2000); //기존 예외처리와 다르게 sleep에서 깨어나며 예외발생시 //catch문으로 가면서 try문 빠져나오는게 아니라 //sleep()아래 코드 적어도 그대로 실행함 }catch (InterruptedException e){} System.out.print("<<main종료>>"); } } class ThreadEx2 extends Thread{ public void run(){ for(int i=0;i<300;i++){ System.out.print("a"); } System.out.print("<<th1 종료>>"); } } class ThreadEx3 extends Thread{ public void run(){ for(int i=0;i<300;i++){ System.out.print("b"); } System.out.print("<<th2 종료>>"); } }
interrupt() & interruped() :쓰레드의 작업을 취소한다 (on<->off)
-진행 중인 쓰레드의 작업이 끝나기 전에 취소시켜야할 때 사용
-예를 들면, 다운로드 중간에 다운로드를 포기 하는 등
-interrupt()는 쓰레드의 작업 중지를 요청 하는 것이지 강제로 종료하는 것은 아니다. (단지 iterrupted상태 [인스턴스 변수]를 바꾸는 것)
-sleep(), wait(), join()에 의해 일시정지 상태에 있을 때, 해당 쓰레드에 대해 interrupt()를 호출하면 상태가 바뀐다.
-InterruptedException이 발생하면, interrupted상태는 false로 자동 초기화 된다.
void interrupt() -쓰레드의 interrupted상태를 false에서 true로 변경
boolean isInterrupted() 쓰레드의 interrupted상태 반환
static boolean interrupted() 현재 쓰레드의 interrupted 상태를 반환하고 false로 변경 (static이므로 자기자신에만)
public class ThreadEX1 { public static void main(String[] args) throws Exception{ ThreadEx2 th1 = new ThreadEx2(); th1.start(); String input = JOptionPane.showInputDialog("아무 값이나 입력하세요"); System.out.println("입력하신 값은 "+input+"입니다."); th1.interrupt(); System.out.println("isInterrupted():"+th1.isInterrupted()); } } class ThreadEx2 extends Thread{ public void run(){ int i=10; while(i!=0 && !isInterrupted()){ System.out.println(i--); for(long x=0;x<250000000L;x++);//시간지연 } } }
suspend(),resume(),stop() - 쓰레드의 정지,재개,종료
각각 쓰레드를 정지하거나, 다시 동작하게 하거나, 즉시 종료시키는 메서드이다. 하지만, suspend와 stop이 교착상태를 일이키기 쉽게 작성되어 있어서 사용이 권장되지 않는다.
public class ThreadEX1 { public static void main(String[] args) throws Exception{ RunImplEx r = new RunImplEx(); Thread th = new Thread(r,"*"); Thread th2 = new Thread(r,"**"); Thread th3 = new Thread(r,"***"); th.start(); th2.start(); th3.start(); try{ Thread.sleep(2000); th.suspend(); Thread.sleep(2000); th2.suspend(); Thread.sleep(3000); th.resume(); Thread.sleep(3000); th.stop(); th2.stop(); Thread.sleep(3000); th3.stop(); }catch (InterruptedException e){} } } class RunImplEx implements Runnable{ @Override public void run() { while(true){ System.out.println(Thread.currentThread().getName()); try{ Thread.sleep(1000); }catch (InterruptedException e){} } } }
기존 suspend와 stop를 이용한 예제이다.간편하게 종료,재개, 정지할 수 있지만 권장되지않음으로 다음과 같이 변경해서 써보도록 하자.
public class ThreadEX1 { public static void main(String[] args) throws Exception{ RunImplEx r = new RunImplEx(); RunImplEx r2 = new RunImplEx(); RunImplEx r3 = new RunImplEx(); Thread th = new Thread(r,"*"); Thread th2 = new Thread(r2,"**"); Thread th3 = new Thread(r3,"***"); th.start(); th2.start(); th3.start(); try{ Thread.sleep(2000); r.suspend(); Thread.sleep(2000); r2.suspend(); Thread.sleep(3000); r.resume(); Thread.sleep(3000); r.stop(); r2.stop(); Thread.sleep(3000); r3.stop(); //Runnable 구현 클래스의 suspend()등을 호출 -> Thread에 영향 //Runnable 구현체로 생성된 Thread는 Runnable을 인스턴스 변수로 포함하고 있고, start()시에 //Runnable 구현체에 구현된 run()을 불러와서 실행 따라서, 위와 같이 쓰레드 참조변수가 아닌 Runnable 구현체 참조변수를 //통해 영향을 주어도 당연히 Thread 작업에 영향을 끼친다. //기존에 suspend()와 stop()등은 쓰레드 클래스에 정의된 것을 이용하는 것.. 그래서 //하나의 구현체로 여러 쓰레드 만들어서 쓰레드 참조변수 단위로 적용했음 }catch (InterruptedException e){} } } class RunImplEx implements Runnable{ boolean suspended = false; // volatile을 앞에 붙여줘야 잘 작동 boolean stopped = false; @Override public void run() { while(!stopped){ if(!suspended) { System.out.println(Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { } } } System.out.println(Thread.currentThread().getName()+"- stopped"); } public void suspend(){ this.suspended=true;} public void resume(){this.suspended=false;} public void stop(){this.stopped=true;} }
public class ThreadEX1 { public static void main(String[] args) throws Exception{ RunImplEx r = new RunImplEx("*"); RunImplEx r2 = new RunImplEx("**"); RunImplEx r3 = new RunImplEx("***"); r.start(); r2.start(); r3.start(); try{ Thread.sleep(2000); r.suspend(); Thread.sleep(2000); r2.suspend(); Thread.sleep(3000); r.resume(); Thread.sleep(3000); r.stop(); r2.stop(); Thread.sleep(3000); r3.stop(); }catch (InterruptedException e){} } } class RunImplEx implements Runnable{ volatile boolean suspended = false; volatile boolean stopped = false; Thread th; RunImplEx(String name){ th = new Thread(this,name); } @Override public void run() { while(!stopped){ if(!suspended) { System.out.println(Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { } } } System.out.println(Thread.currentThread().getName()+"- stopped"); } public void suspend(){ this.suspended=true;} public void resume(){this.suspended=false;} public void stop(){this.stopped=true;} public void start(){th.start();} }
yield() - 다른 쓰레드에게 양보한다.
-자신에게 주어진 실행시간을 다음 차례의 쓰레드에게 양보한다.
-sleep()와 마찬가지로 static 메서드이다.
-스케줄 양보를 통보하는 것 뿐이다. 반드시 적용되는 것은 아님
public class ThreadEX1 { public static void main(String[] args) throws Exception{ RunImplEx r = new RunImplEx("*"); RunImplEx r2 = new RunImplEx("**"); RunImplEx r3 = new RunImplEx("***"); r.start(); r2.start(); r3.start(); try{ Thread.sleep(2000); r.suspend(); Thread.sleep(2000); r2.suspend(); Thread.sleep(3000); r.resume(); Thread.sleep(3000); r.stop(); r2.stop(); Thread.sleep(3000); r3.stop(); }catch (InterruptedException e){} } } class RunImplEx implements Runnable{ volatile boolean suspended = false; volatile boolean stopped = false; Thread th; RunImplEx(String name){ th = new Thread(this,name); } @Override public void run() { String name = th.getName(); while(!stopped){ if(!suspended) { System.out.println(Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { System.out.println(name + " - interrupted"); } }else { Thread.yield(); //suspend가 true라면, 즉 일시정지 상태면 아무의미없어 while문만 돌고 있을 것 //이를 바쁜 대기상태라고 하는데, 이럴때 yield를 호출해서 양보하면 좀 더 효율적이다. } } System.out.println(Thread.currentThread().getName()+"- stopped"); } public void suspend(){ this.suspended=true; th.interrupt(); //suspend가 false일때 중간에 sleep상태에서 바로 깨우려고 System.out.println(th.getName()+" - interrupt() by suspend()"); } public void resume(){this.suspended=false;} public void stop(){ this.stopped=true; th.interrupt(); //stop과 suspend가 false일때 중간에 sleep상태에서 바로 깨우려고 System.out.println(th.getName()+" - interrupt() by stop()"); } public void start(){th.start();} }
join() - 다른 쓰레드의 작업을 기다린다.
-쓰레드 자신이 하던 작업을 잠시 멈추고 다른 쓰레드가 지정된 시간동안 작업을 수행하도록 기다린다.
(일시정지+양보)
- 작업 중에 다른 쓰레드의 작업이 먼저 수행되어야 할 필요가 있을 때 join()을 사용한다.
-sleep처럼 interrupt()에 의해서 벗어날 수 있으며, join도 try-catch로 감싸야한다.
-sleep과 유사한 점이 많다만, 다른 점은 join은 특정 쓰레드에 대해 작동하는 것이라는 점이다. (no static)
void join()
void join(long millis)
public class ThreadEX1 { public static void main(String[] args) throws Exception{ Threadex gcTh = new Threadex(); gcTh.setDaemon(true); gcTh.start(); int requiredMemory =0; for(int i=0; i<20; i++){ requiredMemory = (int)(Math.random()*10)*20; if(gcTh.freeMemory() < requiredMemory || gcTh.freeMemory() < gcTh.totalMemory()*0.4){ gcTh.interrupt(); try{ gcTh.join(100); //가비지 컬렉션 수행을 기다림 }catch (InterruptedException e){} } gcTh.usedMemory += requiredMemory; System.out.println("usedMemory:"+gcTh.usedMemory); } } } class Threadex extends Thread{ final static int MAX_MEMORY =1000; int usedMemory =0; public void run(){ while(true){ try{ Thread.sleep(10*1000); }catch (InterruptedException e){ System.out.println("Awaken by Interrupted()"); } gc(); //가바지 컬렉션 수행 System.out.println("Grabage 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;} }
*Runnable 구현해서 Thread를 만들면,
- Runnable 구현체 내부의 속성도 Runnable 참조변수로 접근해서 이래저래 변경 -> thread에 영향을 줄 수 있고
- Thread 참조변수로 Thread에 영향을 줄 수 있다.
->Thread가 생성자로 Runnable을 받아줘서 포함관계자너 Thread와 Runnable속성 모두 이용가능
참고자료:
자바의 정석 - 남궁성 저
'언어 > JAVA' 카테고리의 다른 글
자바 - Arrays 클래스와 Comparator (0) 2023.03.23 자바 - Thread(4) 동기화 (0) 2023.03.21 자바 -thread(2) [5~8] (0) 2023.03.20 자바 - thread(1) 기본특성 (1~4) (0) 2023.03.20 자바 - Generics (0) 2023.03.16