본문 바로가기

TIL(Today I Learned)

TIL 240911 - Java(Thread)

바보같은 예외

흠..계산기는 얼추 만들었는데, 결과값이 중복으로 나오는경우를 처리해주는거만 하면 좀더 완벽해질것 같다.


 

내일 자바문법 새로운 발제가 있는날이다.

적어도 내일까진 강의를 끝까지 마무리 해야겠다고 생각했다. 그래서 오늘은 강의 듣고 개념정리 위주.

 

블로그 Java 카테고리에 상속화에 관련된 개념도 정리해 놓았다. 아무튼 5주차 강의 내용들


1. 데몬쓰레드

보이지 않는 곳(background)에서 실행되는 낮은 우선순위를 가진 쓰레드
보조적인 역할을 담당. 메모리영역을 정리해주는 가비지 컬렉터(GC) 등

Runnable 쓰레드명 으로 선언한 뒤, 실행문 작성후

Thread thread = new Thread(demon);

으로 쓰레드를 생성한다. 이후 생성된 쓰레드에

thread.setDaemon(true); 라고 작성해주면 데몬쓰레드가 됨

데몬의 특징
1. 우선순위가 낮다. 
2. 상대적으로 다른 쓰레드에 비해 리소스를 적게 할당받는다.
3. 그래서 메인쓰레드의 실행문이 끝나면 데몬쓰레드가 할게남아도 끝난다.

예)

public class Main {
    public static void main(String[] args) {
        Runnable demon = () -> {
            for (int i = 0; i < 1000000; i++) {
                System.out.println("demon");
            }
        };

        Thread thread = new Thread(demon);
        thread.setDaemon(true); //우선순위가 낮은 데몬으로 설정함? true

        thread.start(); //실행하면 위의 데몬쓰레드가 돌아갈 것임

        for (int i = 0; i < 100; i++) {
            System.out.println("task"); //하지만 우선순위가 높은 메인쓰레드가 이걸 끝내면
        } //데몬쓰레드가 못끝내도 메인쓰레드는 끝을 낸다.
    }
}

2. 사용자쓰레드?

보이는곳(foreground)에서 실행되는 높은 우선순위를 가진 쓰레드(데몬쓰레드이외)
대표적으로는 메인쓰레드
기존에 우리가 직접 만들었던 쓰레드들이 다 사용자 쓰레드이다.(Runnable 로 task 정의하고, 생성한것)
사용자쓰레드의 작업이 끝나면 데몬쓰레드도 자동으로 종료시킴

3.우선순위?

최대 우선순위 10

최소 우선순위 1

세팅을 하지 않으면 보통우선순위 5정도로 세팅됨

예)

public class Main {
    public static void main(String[] args) {
        Runnable task1 = () -> {
            for (int i = 0; i < 100; i++) {
                Systehttp://m.out.print("$");
            }
        };

        Runnable task2 = () -> {
            for (int i = 0; i < 100; i++) {
                Systehttp://m.out.print("*");
            }
        };

        Thread thread1 = new Thread(task1);
        thread1.setPriority(8);
        int threadPriority = thread1.getPriority();
        System.out.println("threadPriority = " + threadPriority);

        Thread thread2 = new Thread(task2);
        thread2.setPriority(2);

        thread1.start();
        thread2.start();
    }
}


thread1 이 우선순위 8정도로 세팅되어있고 thread2 가 우선순위  2정도로 되어있어서
8이 2보다 먼저 끝날 가능성이 '높다' (무조건은 아니라는소리)

그리고 위의 코드같은 경우에는 너무 가벼운 코드라 사실 우열을 가리기 힘든 속도로 끝남(8쪽이 약간빠른경향)

4. 쓰레드 그룹

쓰레드가 100개라면 전부다 일일이 관리할 수 없다.
모든 쓰레드들은 반드시 하나의 그룹에 포함되어 있어야 한다.
우리가 만드는 쓰레드는 main메서드의 메인쓰레드에 자동으로 그룹화가 됨

예)

public class Main {
    public static void main(String[] args) {
        Runnable task = () -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    break;
                }
            }
            System.out.println(Thread.currentThread().getName() + " Interrupted");
        };

        // ThreadGroup 클래스로 객체를 만듭니다.
        ThreadGroup group1 = new ThreadGroup("Group1");

        // Thread 객체 생성시 첫번째 매개변수로 넣어줍니다.
        // Thread(ThreadGroup group, Runnable target, String name)
        Thread thread1 = new Thread(group1, task, "Thread 1");
        Thread thread2 = new Thread(group1, task, "Thread 2");

        // Thread에 ThreadGroup 이 할당된것을 확인할 수 있습니다.
        System.out.println("Group of thread1 : " + thread1.getThreadGroup().getName());
        System.out.println("Group of thread2 : " + thread2.getThreadGroup().getName());

        thread1.start();
        thread2.start();

        try {
            // 현재 쓰레드를 지정된 시간동안 멈추게 합니다.
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // interrupt()는 일시정지 상태인 쓰레드를 실행대기 상태로 만듭니다.
        group1.interrupt();

    }
}


전체 쓰레드그룹 코드는 이렇다

여기서 

        // ThreadGroup 클래스로 객체를 만듭니다.
        ThreadGroup group1 = new ThreadGroup("Group1");


이 부분에서 그룹을 만든다.

        // Thread 객체 생성시 첫번째 매개변수로 넣어줍니다.
        // Thread(ThreadGroup group, Runnable target, String name)
        Thread thread1 = new Thread(group1, task, "Thread 1");
        Thread thread2 = new Thread(group1, task, "Thread 2");


이부분에서 쓰레드를 2개 생성하는데 group1이라는 쓰레드그룹에 넣는다고 지정한다.

 

        // Thread에 ThreadGroup 이 할당된것을 확인할 수 있습니다.
        System.out.println("Group of thread1 : " + thread1.getThreadGroup().getName());
        System.out.println("Group of thread2 : " + thread2.getThreadGroup().getName());
        thread1.start();
        thread2.start();


다음코드에서 쓰레드 이름에 대한 정보를 print 로 출력할 수 있고,
그 밑에서 각각의 쓰레드를 시작한다.
시작 후엔 

        Runnable task = () -> {
            while (!Thread.currentThread().isInterrupted()) { //여기 쓰레드는 아래 선언된 쓰레드들이
                try { //interrupt 가 아닐때 동안 로직이 수행됨
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    break;
                }
            }
            System.out.println(Thread.currentThread().getName() + " Interrupted");
        };


이 부분으로 와서 다시 진행함
이 task를 진행한 직후엔 다시 아래의

        try {
            // 현재 쓰레드를 지정된 시간동안 멈추게 합니다.
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


이 부분을 시작한다. 그리고 5000ms 즉 5초가 지나면

        // interrupt()는 일시정지 상태인 쓰레드를 실행대기 상태로 만듭니다.
        group1.interrupt();


그룹에 든 쓰레드 모두를 실행대기 상태로 만든다.

결과적으로

 

1. 그룹화 선언
2. 쓰레드 2개 선언
3. 쓰레드 이름 확인 및 2개의 쓰레드 task 가동
4. 가동직후 try catch문에서 5초간 대기
5. 5초이후 그룹화되어있는 모든 쓰레드들 interrupt 시킴
6. 3번에서 가동된 테스크는 interrupt 당할때 까지 무한가동


결론. 그룹쓰레드가 시작되고 5초동안 열심히 돌다가 멈춘다


5. 쓰레드 상태?

쓰레드 객체생성(new) - start() - 실행대기(Runnable) <- 반복 -> 실행 - 종료

결국 핵심적인 부분은
실행대기(Runnable) - 실행 - 일시정지 - 실행대기 - 실행 - 일시정지 - 실행대기 ...

쓰레드 상태표현
1. 객체 생성(NEW) : 생성, 아직 start() 호출전
2. 실행대기(RUNNABLE) : 실행 상태로 언제든지 갈수있는 상태
3. 일시정지(WAITING) : 다른 쓰레드가 통지 notify 할때까지 기다리는 상태
4. 일시정지(TIME_WAITING) : 주어진 시간동안 기다리는 상태
5. 일시정지(BLOCKED) : 사용하고자 하는 객체의 Lock이 풀릴때까지 기다리는 상태
6. 종료(TERMINATED) : 쓰레드의 작업이 종료된 상태

예)

public class Main {
    public static void main(String[] args) {
        Runnable task = () -> {
            try { //쓰레드만 사용했을 경우엔 위험이 있어서 Thread라는 클래스에서 애초에 throws ~ 하는 예외처리를 해둠
                Thread.sleep(2000);//기본제공 메서드. Thread 라는 클래스 안에 static sleep() 메서드가 있음
            } catch (InterruptedException e) { //슬립이 2초동안 자는건데, 이상태에서 interrupt를 호출하면 무조건
                e.printStackTrace();            //실행대기 상태로 보내버림. 그럼 sleep이 깨질때 예외상황이 발생됨
                                                //sleep 깨질때 예외가 발생하면 저 exception을 써라! 라는 이유로
                                                //try catch문을 씀
            }
            System.out.println("task : " + Thread.currentThread().getName());
        };

        Thread thread = new Thread(task, "Thread");
        thread.start(); //NEW -> RUNNABLE

        try {
            thread.sleep(1000); //생성한 인스턴스에 직업 sleep을 걸면 Thread 를 참조해서 하고있어서 노란줄뜸
            //Thread.sleep(1000); // 이거나 위에거나 같은거임. 왜냐면 static 메서드이기 때문에
            System.out.println("sleep(1000) : " + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 


interrupt()

일시정지 상태인 쓰레드를 실행대기 상태로 만듦

sleep 하고있을때 interrupt로 방해해서 깨우면 예외가 발생하는데

예1)

public class Main {
    public static void main(String[] args) {
        Runnable task = () -> {
            try {
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace(); //sleep할땐 예외처리
            }
            System.out.println("task : " + Thread.currentThread().getName());
        };

        Thread thread = new Thread(task, "Thread");
        thread.start();

        thread.interrupt();

        System.out.println("thread.isInterrupted() = " + thread.isInterrupted());
        //현재 interrupt 인 상태인가를 확인하는 메서드
    }
}


예2)

public class Main {
    public static void main(String[] args) {
        Runnable task = () -> {
            while (!Thread.currentThread().isInterrupted()) { //지금 interrupt 상태니? 아닐때만 아래구문을 함
                try {
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    break; //그리고 예외가 발생하면 break
                }
            }
            System.out.println("task : " + Thread.currentThread().getName());
        };

        Thread thread = new Thread(task, "Thread");
        thread.start();

        thread.interrupt();

        System.out.println("thread.isInterrupted() = " + thread.isInterrupted());

    }
}

 

예1 도 작동은 하지만, 콘솔창에서 불편한 예외 내용을 볼 수 있다.

 

이걸 막기 위해서, while 반복문으로 조건에 sleep()하다가 interrupt() 했니? 를 체크하고 지금 interrupt() 상태라면

반복문을 안하는것으로 좀더 깔끔하게 처리할 수 있다.

 

오늘 한 일

1. 아침에 알고리즘공부

2. Lv 3 계산기의 Enum, Generic 개념 적용

3. 자바 여러 문법강의(Thread에 대해)

 

내일 할 일

1. 새로운 개인과제 받고나서 분석

2. 분석 끝나면 조건에 맞춰서 작성시작

3. 시간이 뜨면 강의도 듣기(가능하면 오후2시전에 남은강의는 전부 수강)

 

오늘도 화이팅!

728x90
반응형

'TIL(Today I Learned)' 카테고리의 다른 글

TIL 240913 - 트러블 슈팅(feat.숫자야구게임)  (6) 2024.09.13
TIL 240912 - 숫자야구게임 1일차  (0) 2024.09.12
TIL 240910  (2) 2024.09.10
TIL 240909  (3) 2024.09.09
TIL 240905  (2) 2024.09.05