ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] 멀티 스레드 - 우선순위, 동기화 메소드
    CSE/Java 2015. 12. 12. 14:26

    멀티 스레드는 여러 절로 구성되어 있습니다.


      

    Intro

    작업스레드

    스레드 우선순위 & 동기화 메소드와 동기화 블록





     

    스레드 우선순위

     멀티 스레드는 동시성(Concurrency) 또는 병렬성(Parallelism)으로 실행되기 때문에 이 용어들에 대해 정확히 이해하는 것이 좋습니다.


     동시성은 멀티 작업을 위해 하나의 코어에서 멀티 스레드가 번갈아가며 실행하는 성질을 말합니다.


     병렬성은 멀티 작업을 위해 멀티 코어에서 개별 스레드를 동시에 실행하는 성질을 말합니다.




     






     스레드의 개수가 코어의 수보다 많을 경우, 스레드를 어떤 순서에 의해 동시성으로 실행할 것인가를 결정해야 하는데, 이것을 스레드 스케줄링이라고 합니다. 스케줄링에 의해 스레드들은 아주 짧은 시간에 번갈아가면서 run() 메소드를 실행시킵니다.

     


     자바의 스레드 스케줄링은 우선순위(Priority) 방식과 순환 할당(Round-Robin) 방식을 사용합니다. 


     우선순위 방식은 우선순위가 높은 스레드가 실행 상태를 더 많이 가지도록 스케줄링하는 것을 말합니다.


     순환 할당 방식은 시간 할당량(Time Slice)를 정해서 하나의 스레드를 정해진 시간만큼 실행시키고 다시 다른 스레드를 실행하는 방식을 말합니다.


     우선순위 방식은 스레드 객체에 우선순위 번호를 부여할 수 있기 때문에 개발자가 코드로 제어할 수 있지만, 순환 할당 방식은 자바 가상 기계(JVM)에 의해 정해지기 때문에 코드로 제어할 수 없습니다.




     우선순위 방식에서 우선순위는 1에서부터 10까지 부여되는데, 1이 가장 우선순위가 낮고, 10이 가장 높습니다. 우선순위를 부여하지 않으면 모든 스레드들은 기본적으로 5의 우선순위를 할당받습니다.


     우선순위 변경 메소드는 Thread 클래스가 제공하는 setPriority() 메소드를 이용하면 됩니다.



    1
    thread.setPriority(Priority);
    cs




     우선순위의 파라미터는 1~10까지의 값을 직접 주어도 좋지만, 코드의 가독성을 높이기 위해 Thread 클래스의 상수를 사용할 수도 있습니다.



    1
    2
    3
    thread.setPriority(Thread.MAX_PRIORITY);
    thread.setPriority(Thread.MIN_PRIORITY);
    thread.setPriority(Thread.NORM_PRIORITY);
    cs






     이 상수는 각각 10, 1, 5의 값을 가지고 있습니다. 흔히 쿼드 코어에서는 4개의 스레드가 병렬성으로 실행될 수 있기 때문에 4개 이하의 스레드를 실행할 경우에는 우선순위 방식이 크게 영향을 미치지 못합니다. 최소한 5개 이상의 스레드가 실행되어야 우선순위에 영향을 받습니다.


     다음은 스레드 10개를 생성하고 20억번 루핑을 누가 더 빨리 끝내는가를 테스트하는 예제입니다. Thread10만 가장 높은 우선순위를 주었고, 나머지는 가장 낮은 우선순위로 설정했습니다.





     * CalcThread.java


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    package mT;
     
    public class CalcThread extends Thread {
        public CalcThread(String name) {
            setName(name);
        }
        
        @Override
        public void run() {
            for (long i = 0; i < 2000000000; i++) {
                
            }
            System.out.println(getName());
        }
    }
     
    cs



     * PriorityExam.java


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    package mT;
     
    public class PriorityExam {
        public static void main(String[] args) {
            for (int i = 1; i <= 10; i++) {
                Thread thread = new CalcThread("thread" + i);
     
                if (i != 10) {
                    thread.setPriority(Thread.MIN_PRIORITY);
                } else {
                    thread.setPriority(Thread.MAX_PRIORITY);
                }
     
                thread.start();
            }
        }
     
    }
     
    cs

















    동기화 메소드와 동기화 블록

     공유 객체를 사용할 때의 주의할 점

      싱글 스레드 프로그램에서는 한 개의 스레드가 객체를 독차지해서 사용하면 되지만, 멀티 스레드 프로그램에서는 스레드들이 객체를 공유해서 작업해야 하는 경우가 있습니다. 흔히 다루는 Computer Science에서 다루는 문제점으로 세마포어, C-P 문제, 락 등 여러 해결점이 있습니다.



      예제를 통해서 공유 객체의 문제점을 직접 보도록 합시다.




     * MainThreadExam.java


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    package mT;
     
    public class MainThreadExam {
        public static void main(String[] args) {
            Calculator calc = new Calculator();
     
            User1 user1 = new User1();
            user1.setCalculator(calc);
            user1.start();
     
            User2 user2 = new User2();
            user2.setCalculator(calc);
            user2.start();
        }
     
    }
     
    cs





     * Calculator.java


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    package mT;
     
    public class Calculator {
        private int memory;
     
        public int getMemory() {
            return memory;
        }
     
        public void setMemory(int memory) {
            this.memory = memory;
            try {
                Thread.sleep(2000); // 2초간 sleep
            } catch (Exception e) {
            }
     
            System.out.println(Thread.currentThread().getName() + ": " + this.memory);
        }
    }
     
    cs





     * User1.java


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    package mT;
     
    public class User1 extends Thread {
        private Calculator calc;
     
        public void setCalculator(Calculator calc) {
            this.setName("calcUser1");
            this.calc = calc;
        }
     
        public void run() {
            calc.setMemory(100);
        }
    }
     
    cs





     * User2.java


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    package mT;
     
    public class User2 extends Thread {
        private Calculator calc;
     
        public void setCalculator(Calculator calc) {
            this.setName("calcUser2");
            this.calc = calc;
        }
     
        public void run() {
            calc.setMemory(50);
        }
     
    }
     
    cs












     동기화 메소드 및 동기화 블록

      스레드가 사용 중인 객체를 다른 스레드가 변경할 수 없도록 하려면 스레드 작업이 끝날 때까지 객체에 잠금을 걸어서 다른 스레드가 사용할 수 없도록 해야 합니다. 멀티 스레드 프로그램에서 단 하나의 스레드만 실행할 수 있는 코드 영역을 임계 영역(critical Section)이라고 합니다.


      자바는 임계 영역을 지정하기 위해 동기화(synchronized) 메소드와 동기화 블록을 제공합니다. 동기화 메소드를 만드는 방법은 다음과 같이 메소드 선언에 synchronized 키워드를 붙이면 됩니다.



    1
    2
    3
    4
    public synchronized void method() {
        critical Section; // 
    }
     
    cs






      동기화 메소드는 메소드 전체 내용이 임계 영역이므로 스레드가 동기화 메소드를 실행하는 즉시 객체에는 잠금이 일어나고, 스레드가 동기화 메소드를 종료하면 잠금 해제 됩니다. 메소드 전체 내용이 아니라, 일부 내용만 임계 영역으로 만들고 싶다면 동기화 블록을 만들면 됩니다.




    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public void method() {
        ...
     
        synchronized(sharedObject) {
            critical Section;
        }
     
        ...
     
    }
    cs





      동기화 블록의 외부 코드들은 여러 스레드가 동시에 실행할 수 있지만, 동기화 블록의 내부 코드는 임계 영역이므로 한 번에 한 스레드만 실행할 수 있고 다른 스레드는 실행 할 수 없습니다. 만약 동기화 메소드와 블록이 여러 개 있을 경우, 스레드가 이들 중 하나를 실행할 때 다른 스레드는 해당 메소드는 물론이고 다른 동기화 메소드 및 블록도 실행할 수 없습니다. 하지만 일반 메소드는 실행이 언제든 가능합니다.














      다음 예제는 이전 예제에서 문제가 된 공유 객체인 Calculator를 수정한 것입니다.



     * Calculator.java


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package mT;
     
    public class Calculator {
        private int memory;
     
        public int getMemory() {
            return memory;
        }
     
        public synchronized void setMemory(int memory) {
            this.memory = memory;
            try {
                Thread.sleep(2000); // 2초간 sleep
            } catch (Exception e) {
            }
     
            System.out.println(Thread.currentThread().getName() + ": " + this.memory);
        }
    }
     
    cs

















     * 이 포스트은 서적 '이것이 자바다' 를 참고하여 작성한 포스트입니다.

    댓글

Designed by Tistory.