본문 바로가기
Java

Thread-2

by 스르나 2021. 2. 11.
  1. 우선순위
  2. 쓰레드 그룹
  3. 데몬 쓰레드

 

1. 우선순위

쓰레드에는 우선순위라는 개념이 있다. 우선순위는 쓰레드간의 중요도에 따라 지정할 수 있다.

예를들어 채팅과 파일전송을 하는 프로그램을 예로 들어보자. 채팅같은경우는 아주빠른 시간안에 응답을 해줘야하고, 파일전송은 채팅보다는 비교적 느려도 괜찮다. 그런데 만약 2가지의 작업의 우선순위가 같다면 채팅의 응답속도가 느려질 수 있다. 이럴때 채팅의 우선순위를 보다 빠르게 하면 우리가 채팅을하는데 큰 문제를 못느낄것이다.

 

그러면 자바 Thread에서 어떻게 우선순위를 지정하는지 보자.

 

Thread thread=new Thread(()->{
           // do something 
        });
        
thread.setPriority(5); // 1~10
        
thread.start();

자바 Thread의 우선순위는 Thread의 priority라는 정수형 변수를 바꾸는것으로 정한다. 위의 코드에서 보다싶이 범위는 1~10으로 정해져 있는데 default값은 5이다. 그리고 우선순위 숫자가 낮을 수록 우선순위가 높은것이다.(1이 10보다 먼저 실행된다는것)

 

그럼 이렇게 정한 우선순위가 정말 제대로 실행되는지 예시코드와 결과를 보자.

 

public class PriorityThread {

    public void test(){

        Thread th=new Thread(()->{
            for(int i=0;i<300;i++){
                if(i%10==0){
                    System.out.println();
                }
                System.out.print("-");
            }
        });

        Thread th2=new Thread(()->{
            for(int i=0;i<300;i++){
                if(i%10==0){
                    System.out.println();
                }
                System.out.print("|");
            }
        });

        th.setPriority(1);
        th2.setPriority(10);

        th.start();
        th2.start();
    }
}

 

실행결과를 보면 우선순위가 낮은 '|' 가 마지막에 완료된것을 볼 수 있다. 또한 우선순위가 높다고해서 무조건 먼저 실행되는것도 아니다. 다만 보다 먼저 실행이 완료될 가능성이 높아진다고 이해하면 된다.

 

사실 우선순위는 우리가 임의로 정한다고 우리가 원하는대로 작동하지 않을 가능성이 있다. 이유는 자바의 쓰레드는 OS에 종속적으로 실행되기 때문에 OS의 우선순위 정책에 따르게 된다. 그렇기 때문에 실행하는 컴퓨터의 상황에 따라 달라질수있다.

 

그러므로 우선순위를 꼭 정해서 원하는대로 실행되기를 바란다면 차라리 PriorityQueue에 넣고 우선순위가 높은것이 먼저 실행되도록 하는것이 보다 좋은방법이다.

 

 

//쓰레드
public class PriorityThread implements Comparable<PriorityThread>{
    private Thread thread;
    private int priority=5;

    @Override
    public int compareTo(PriorityThread o) {
        if(this.priority>=o.priority) return 1;
        return 0;
    }

    public void setThread(Runnable runnable){
        thread=new Thread(runnable);
    }

    public void setPriority(int priority){
        this.priority=priority;
    }

    public void start(){
        thread.start();
    }
}

 

public class Main {
    public static void main(String[] args) {
        PriorityQueue<PriorityThread> queue=new PriorityQueue<>(); //우선순위 큐를 만든다.
        PriorityThread thread=new PriorityThread();
        PriorityThread thread2=new PriorityThread();

        thread.setThread(()->{
            for(int i=0;i<1000;i++){
                System.out.println("O");
            }
        });

        thread2.setThread(()->{
            for(int i=0;i<1000;i++){
                System.out.println("X");
            }
        });

        //각각의 우선순위를 정함
        thread.setPriority(1);
        thread2.setPriority(10);

        //우선순위 큐에 넣는다.
        queue.add(thread);
        queue.add(thread2);


        while(!queue.isEmpty()){
            queue.poll().start();
        }
    }
}

위처럼 하면 우선순위에 맟춰 무조선 우선순위가 높은게 먼저실행이 된다.

 

2. 쓰레드 그룹

자바의 모든 쓰레드는 각자의 그룹에 속해져있다.

 

쓰레드 그룹은 서로 관견된 쓰레드끼리 묶음으로서 쓰레드를 관리하기 위해사용된다. 쓰레드 그룹은 상위 그룹 하위 그룹으로 정할 수 있는데 이렇게 그룹들을 상위 하위로 묶으면 자신의 속한그룹, 하위그룹은 변경이 가능하지만 다른 그룹은 변경할 수 없다.

 

우선 우리는 자바의 쓰레드는 따로 생성하지 않으면 Main쓰레드가 먼저 만들어진다는것을 알고있는데 이 Main쓰레드 또한 Main쓰레드 그룹에 들어간다.

 

그리고 Main쓰레드의 상위 그룹인 System그룹이 있는데 이 System그룹은 가비지 컬렉터같은것이 들어간다. System그룹은 우리가 직접 건드릴일이 거의 없으니 신경쓰기 않아도 된다.

 

그럼 쓰레드 그룹을 만드는 방법을 보자.

 

public class MyThreadGroup {
    public void test(){
        ThreadGroup group=new ThreadGroup("group1");
        
        ThreadGroup sonGroup=new ThreadGroup(group,"sonGroup");
    }
}

우선 처음에 만든 group이라는 쓰레드는 생성자의 매개변수로 문자열을 줬는데 이 문자열이 쓰레드그룹의 이름이된다.

 

그담의 sonGroup의 생성자에는 위에만든 group과 문자열을 줬는데, 여기서 group은 상위 그룹이된다.

 

그럼 group의 생성자에는 상위그룹을 주지않았는데 이 group은 상위가 현재 그룹 생성자를 실행한 쓰레드의 그룹에 들어가게 된다. 즉, 여기서는 Main쓰레드 그룹으로 들어가는 것이다.

 

public class MyThreadGroup {
    public void test(){
        ThreadGroup group=new ThreadGroup("group1");

        ThreadGroup sonGroup=new ThreadGroup(group,"sonGroup");


        new Thread(group,()->{
            System.out.println(Thread.currentThread().getThreadGroup().getName());
            System.out.println(Thread.currentThread().getThreadGroup().getParent().getName());
        }).start();

        new Thread(()->{
            System.out.println(Thread.currentThread().getThreadGroup().getName());
        }).start();

        new Thread(sonGroup,()->{
            System.out.println(Thread.currentThread().getThreadGroup().getName());
            System.out.println(Thread.currentThread().getThreadGroup().getParent().getName());
        }).start();
    }
}

 

각 그룹의 이름을 출력해보면 중간에 Thread의 생성자에 그룹을 주지않은것은 main그룹으로 들어간다는 것을 알 수 있다.

그리고 각 쓰레드 그룹의 조상을 가져온것을 보면 group1의 조상은 main이고, songroup은 group1으로 정해진것 또한 확인할 수 있다.

 

3. 데몬 쓰레드

데몬 쓰레드는 쓰레드를 위한 쓰레드 즉, 보조역할을 하는 쓰레드라고 생각하면된다.

예를들어 워드프로세서의 자동저장같은 기능을 의미한다. 워드프로세서에서 사용자의 입력을 받는 것이 main쓰레드라면 이 자동저장같은 기능은 데몬 쓰레드로 하면된다.

 

여기서 한가지 의문이 들 수있다. 자동저장을 굳이 데몬 쓰레드로 하지않고 그냥 쓰레드로 해도 되지않을까 생각이 들 수 있는데 데몬 쓰레드의 생성과 소멸에서 일반 쓰레드와의 차이가 있다.

 

우선 데몬쓰레드는 일반 쓰레드를 만들듯이 작성하되, 실행전에 setDaemon(boolean on)함수에서 true 매개변수를 전해주면 데몬쓰레드로 생성이 된다.

다음으로 소멸인데 이 소멸이 데몬 쓰레드의 키포인트이다. 일단 데몬 쓰레드는 일반 쓰레드가 종료되면 자신도 자동으로 종료된다.

 

위의 워드프로세서의 예시에서 만약 자동저장을 일반 쓰레드로 하면 워드프로세서를 종료해도 자동저장이 계속 실행될 수도있기 때문에 데몬 쓰레드로 하는것이다.

 

그럼 바로 예시 코드를보자

 

public class WordThread extends Thread {
    
    // 메인으로 돌아가는 쓰레드
    @Override
    public void run(){
        int k=0;
        while(true){
            try{
                Thread.sleep(1000);
            }catch (Exception e){
                e.printStackTrace();
            }
            k++;
            if(k==10){
                System.out.println("프로그램이 종료됩니다.");
                break;
            }
            System.out.println("글을 작성중");
        }
    }

    // 데몬 쓰레드를 만들고 실행시키는 메소드
    public void startBackground(){
        Thread backGround=new Thread(()->{
            while(true){
                try{
                    Thread.sleep(3000);
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("글을 저장했습니다");
            }
        });
        backGround.setDaemon(true);
        backGround.start();
    }
}

 

아래의 startBackground 메소드에서 보면 일반 쓰레드와 같지만 중간에 setDaemon(true)로 설정하여 데몬 쓰레드로 만들었다. 즉, 위의 run메소드가 종료되면 자동을 종료되는 쓰레드이다.

 

만약 저기서 setDaemon을 설정하지 않으면 run이 종료 되어도 startBackground는 종료되지 않고 계속 실행이 된다.

 

실형 결과를 보자

 

 

그럼 setDaemon을 주석처리하고 실행 결과를 보자

 

보다싶이 "프로그램이 종료됩니다"이후에도 background가 돌아가는 것을 확인할 수 있다.

'Java' 카테고리의 다른 글

Iterator,Iterable  (0) 2021.02.13
Thread-3  (0) 2021.02.12
Thread-1  (0) 2021.02.08
Interface  (0) 2020.09.21
Generic  (0) 2020.07.21