ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 자바 - 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
Designed by Tistory.