ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 쓰레드(1)
    Computer Science/JAVA 2021. 8. 20. 22:38

    1️⃣ 프로세스와 쓰레드

    프로세스란 간단히 말해 실행중인 프로그램이다. 프로그램을 실행하면 OS로부터 실행에 필요한 자원을 할당받아 프로세스가 된다. 프로세스는 프로그램을 수행하는 데 필요한 데이터와 메모리 등의 자원 그리고 쓰레드로 구성되어 있으며 프로세스의 자원을 이용해서 실제로 작업을 수행하는 것이 바로 쓰레드이다.

    그래서 모든 프로세스에는 최소한 하나 이상의 쓰레드가 존재하며 둘 이상의 쓰레드를 가진 프로세스를 '멀티 쓰레드 프로세스'라고 한다.

    하나의 프로세스가 가질 수 있는 쓰레드의 개수는 제한되어 있지 않지만 쓰레드가 작업을 수행하는데 개별적인 메모리 공간을 필요로 하기 때문에 프로세스의 메모리 한계에 따라 생성할 수 있는 쓰레드의 수가 결정된다.

    [ 멀티태스킹과 멀티 쓰레딩 ]

    멀티태스킹은 여러 개의 프로세스가 동시에 실행되는 것을 뜻한다. 이와 마찬가지로 멀티 쓰레딩은 하나의 프로세스에서 여러 쓰레드가 동시에 작업을 수행하는 것이다. CPU의 코어가 한 번에 단 하나의 작업만 수행할 수 있으므로, 실제로 동시에 처리되는 작업의 개수는 코어의 개수와 일치한다.

    그러나 처리해야하는 쓰레드의 수는 언제나 코어의 개수보다 많기 때문에, 각 코어가 아주 짧은 시간 동안 여러 작업을 번갈아가며 수행함으로써 여러 작업들이 모두 동시에 수행되는 것처럼 보이게 한다.

    [ 멀티쓰레딩의 장단점 ]

    멀티 쓰레딩의 장점

    • CPU의 사용률을 향상시킨다

    • 자원을 보다 효율적으로 사용할 수 있다.

    • 사용자에 대한 응답성이 향상된다.

    • 작업이 분리되어 코드가 간결해진다.

    여러 사용자에게 서비스를 해주는 서버 프로그램의 경우 멀티 쓰레드로 작성하는 것은 필수적이어서 하나의 서버 프로세스가 여러 개의 쓰레드를 생성해서 쓰레드와 사용자의 요청이 일대일로 처리되도록 프로그래밍해야 한다.

    만약 싱글 쓰레드로 서버 프로그램을 작성한다면 사용자의 요청마다 새로운 프로세스를 생성해야 하는데 프로세스를 생성하는 것은 쓰레드를 생성하는 것에 비해 더 많은 시간과 메모리 공간이 필요하기 때문에 많은 수의 사용자 요청을 서비스 하기 어렵다.

    멀티 쓰레딩 단점

    • 멀티 쓰레드 프로세스는 여러 쓰레드가 같은 프로세스 내에서 자원을 공유하면서 작업을 하기 때문에 발생할 수 있는 동기화, 교착상태와 같은 문제들을 고려해서 프로그래밍 해야 한다.

    2️⃣ 쓰레드의 구현과 실행

    쓰레드를 구현하는 방법은 Thread 클래스를 상속받는 방법과 Runnable 인터페이스를 구현하는 방법이 있다.

    어느쪽을 사용해도 괜찮지만, Runnable 인터페이스를 구현하는 방법이 일반적이다.

    Runnable 인터페이스를 구현하는 쪽이 재사용성이 높고 일관성을 유지할 수 있기 때문에 보다 객체지향적이다.

    쓰레드 구현 방법 2가지 예제

    public class ThreadEx1 {
        public static void main(String[] args) {
            ThreadEx1_1 t1 = new ThreadEx1_1();
            Runnable r = new ThreadEx1_2();
    
            Thread t2 = new Thread(r);
    
            t1.start();
            t2.start();
        }
    }
    
    class ThreadEx1_1 extends Thread {
        @Override
        public void run() {
            for(int i = 0; i < 5; i++) {
                System.out.println(getName());
            }
        }
    }
    
    class ThreadEx1_2 implements Runnable {
    
        @Override
        public void run() {
            for(int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName());
            }
        }
    }

    [ 쓰레드 실행 - start() ]

    쓰레드를 생성했다고 해서 자동으로 실행되는 것은 아니고, start()를 호출해야만 쓰레드가 실행된다.

    그리고 한 번 실행이 종료된 쓰레드는 다시 실행할 수 없다. 즉, 하나의 쓰레드에 대해 start()는 한 번만 호출될 수 있다.

    3️⃣ start(), run()

    main 메서드에서 run()을 호출하는 것은 생성된 쓰레드를 실행시키는 것이 아니라 단순히 클래스에 선언된 메서드를 호출하는 것이다. 반면 start()는 새로운 쓰레드가 작업을 실행하는데 필요한 호출스택을 생성한 다음에 run()을 호출해서, 생성된 호출 스택에 run()이 첫번째로 올라가게 한다.

    모든 쓰레드는 독립적인 작업을 수행하기 위해 자신만의 호출스택을 필요로 하기 때문에, 새로운 쓰레드를 생성하고 실행시킬 때마다 새로운 호출스택이 생성되고 쓰레드가 종료되면 작업에 사용된 호출스택은 소멸된다.

    스케줄러는 실행대기중인 쓰레드들의 우선순위를 고려하여 실행순서와 실행시간을 결정하고, 각 쓰레드들은 작성된 스케줄에 따라 자신의 순서가 되면 지정된 시간동안 작업을 수행한다.

    주어진 시간동안 작업을 마치지 못한 쓰레드는 다시 자신의 차례가 돌아올 때까지 대기상태로 있게 되며, 작업을 마친 쓰레드는 호출스택이 모두 비워지면서 이 쓰레드가 사용하던 호출스택은 사라지게 된다.

    [ main 쓰레드 ]

    평소에 main 메서드의 작업을 수행하는 것도 쓰레드이며, 이를 main 쓰레드라고 한다. 지금까지는 main 메서드가 수행을 마치면 프로그램이 종료되었지만, main 메서드가 수행을 마쳤다고 하더라고 다른 쓰레드가 아직 작업을 마치지 않은 상태라면 프로그램이 종료되지 않는다.

    4️⃣ 싱글 쓰레드와 멀티 쓰레드

    하나의 쓰레드로 두 작업을 처리하는 경우는 한 작업을 마친 후에 다른 작업을 시작하지만, 두 개의 쓰레드로 작업 하는 경우에는 짧은 시간동안 2개의 쓰레드가 번갈아 가면서 작업을 수행해서 동시에 두 작업이 처리되는 것과 같이 느끼게 한다.

    싱글 코어에서 단순히 CPU만을 사용하는 계산 작업이라면 오히려 멀티쓰레드보다 싱글쓰레드로 프로그래밍 하는 것이 효율적이다(컨텍스트 스위칭 비용)

    main 쓰레드와 thread1 동시 작업

    public class ThreadEx2 {
        static long startTime;
        public static void main(String[] args) {
            Thread t1 = new Thread(new ThreadEx2_1());
            t1.start();
            startTime = System.currentTimeMillis();
    
    
            for(int i = 0; i < 300; i++)
                System.out.printf("%s", "-");
            System.out.print("소요시간1 : " + (System.currentTimeMillis() - startTime));
            System.out.println("main 종료");
        }
    }
    
    class ThreadEx2_1 implements Runnable {
    
        @Override
        public void run() {
            for(int i = 0; i < 1000; i++)
                System.out.printf("%s", "|");
            System.out.print("소요시간2 : " + (System.currentTimeMillis() - ThreadEx2.startTime));
            System.out.println("thread1 종료");
        }
    }

    • main 쓰레드가 먼저 종료됨

    • "-"와 "|"가 번갈아 가면서 출력됨

    5️⃣ 쓰레드 우선순위

    쓰레드는 우선순위라는 속성을 가지고 있는데, 이 우선순위의 값에 따라 쓰레드가 얻는 실행시간이 달라진다.

    쓰레드가 수행하는 작업의 중요도에 따라 쓰레드의 우선순위를 서로 다르게 지정하여 특정 쓰레드가 더 많은 작업 시간을 가지도록 할 수 있다.

    [ 쓰레드 우선순위 지정하기 ]

    쓰레드가 가질 수 있는 우선순위의 범위는 1 ~ 10이며 숫자가 높을수록 우선순위가 높다, 그리고 쓰레드의 우선순위는 쓰레드를 생성한 쓰레드로부터 상속을 받는다.

    public class ThreadEx3 {
        public static void main(String[] args) {
            Thread t1 = new ThreadEx3_1();
            Thread t2 = new ThreadEx3_2();
    
            t1.setPriority(7);
            System.out.println("t1의 우선순위 : " + t1.getPriority());
            System.out.println("t2의 우선순위 : " + t2.getPriority());
    
            t1.start();
            t2.start();
        }
    }
    
    class ThreadEx3_1 extends Thread {
    
        @Override
        public void run() {
            for(int i = 0; i < 300; i++) {
                System.out.print("-");
                for(int x = 0; x < 10000000; x++);
            }
        }
    }
    
    class ThreadEx3_2 extends Thread {
    
        @Override
        public void run() {
            for(int i = 0; i < 300; i++) {
                System.out.print("|");
                for(int x = 0; x < 10000000; x++);
            }
        }
    }

    t1이 t2보다 우선순위가 높기 때문에 더 많은 시간을 할당 받고 더 빨리 완료합니다.

    6️⃣ 쓰레드 그룹

    쓰레드 그룹은 서로 관련된 쓰레드를 그룹으로 다루기 위한 것으로, 폴더를 생성해서 관련된 파일들을 함께 넣어서 관리하는 것처럼 쓰레드 그룹을 생성해서 쓰레드를 그룹으로 묶어서 관리할 수 있음.

    모든 쓰레드는 반드시 쓰레드 그룹에 포함되어 있어야 하기 때문에, 쓰레드 그룹을 지정하는 생성자를 사용하지 않은 쓰레드는 기본적으로 자신을 생성한 쓰레드와 같은 쓰레드 그룹에 속하게 됨.

    자바 어플리케이션이 실행되면, JVM은 main과 system이라는 쓰레드 그룹을 만들고 JVM 운영에 필요한 쓰레드들을 생성해서 이 쓰레드 그룹에 포함시킴. 우리가 생성하는 모든 쓰레드 그룹은 main 쓰레드 그룹의 하위 쓰레드 그룹이 되며, 쓰레드 그룹을 지정하지 않고 생성한 쓰레드는 자동적으로 main 쓰레드 그룹에 속하게 됨.

    쓰레드 그룹 예제

    public class ThreadEx4 {
        public static void main(String[] args) {
            ThreadGroup main    = Thread.currentThread().getThreadGroup();
            ThreadGroup grp1    = new ThreadGroup("Group1");
            ThreadGroup grp2    = new ThreadGroup("Group2");
    
            ThreadGroup subGrp1 = new ThreadGroup(grp1, "SubGroup1");
    
            grp1.setMaxPriority(3);
    
            Runnable r = () -> {
                try {
                    Thread.sleep(1000);
                } catch(InterruptedException e){}
            };
    
            new Thread(grp1, r, "th1").start();
            new Thread(subGrp1, r, "th2").start();
            new Thread(grp2, r, "th3").start();
    
            main.list();
        }
    }

    쓰레드 그룹과 쓰레드를 생성하고 main.list()를 호출해서 main쓰레드 그룹의 정보를 출력하는 예제.

    7️⃣ 데몬 쓰레드

    데몬 쓰레드는 다른 일반 쓰레드의 작업을 돕는 보조적인 역할을 수행하는 쓰레드이다. 일반 쓰레드가 모두 종료되면 데몬 쓰레드는 강제적으로 종료되는데, 데몬 쓰레드는 일반 쓰레드의 보조 역할을 수행하므로 일반 쓰레드가 모두 종료되고 나면 데몬 쓰레드의 존재 의미가 없기 때문.

    (가비지 컬렉터, 자동 저장, 자동 화면 갱신 등이 있음)

    데몬 쓰레드는 무한루프와 조건문을 이용해서 실행 후 대기하고 있다가 특정 조건이 만족 되면 작업을 수행하고 다시 대기하도록 작성.

    데몬 쓰레드 예제

    public class ThreadEx5 implements Runnable {
        static boolean autoSave = false;
    
        public static void main(String[] args) {
            Thread t = new Thread(new ThreadEx5());
            t.setDaemon(true);
            t.start();
    
            for(int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ignored) {}
                System.out.println(i);
                if(i == 5)
                    autoSave = true;
            }
            System.out.println("프로그램 종료");
        }
    
        @Override
        public void run() {
            while(true) {
                try {
                    Thread.sleep(3 * 1000);
                } catch (InterruptedException ignored) {}
                if(autoSave)
                    autoSave();
            }
        }
    
        public void autoSave() {
            System.out.println("자동저장되었습니다.");
        }
    }

    Uploaded by Notion2Tistory v1.1.0

    'Computer Science > JAVA' 카테고리의 다른 글

    쓰레드(2)  (0) 2021.08.20
    애너테이션  (0) 2021.08.20
    열거형(enums)  (0) 2021.08.20
    Generics  (0) 2021.08.20
    String, StringBuilder, StringBuffer  (0) 2021.08.20

    댓글

Designed by Tistory.