ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] 멀티 스레드 - 작업 스레드
    CSE/Java 2015. 12. 12. 13:12

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

     


    Intro

    작업스레드

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





     

    작업 스레드 생성과 실행

     멀티 스레드로 실행하는 어플리케이션을 개발하려면 먼저 몇 개의 작업을 병렬로 실행할지 결정하고 각 작업별로 스레드를 생성해야 합니다.




     








     어떤 자바 어플리케이션이건 메인 스레드는 반드시 존재하기 때문에 메인 작업 이외에 추가적인 병렬 작업의 수만큼 스레드를 생성하면 됩니다. 


     작업 스레드는 객체로 생성되기 때문에 java.lang.Thread 클래스가 필요합니다. 직접 객체화해서 생성해도 되지만, Thread를 상속하여 하위 클래스로 만들어 사용할 수 있습니다.







     Thread 클래스로부터 직접 생성

      java.lang.Thread 클래스로부터 작업 스레드 객체를 직접 생성하려면 다음과 같이 Runnable을 파라미터로 갖는 생성자를 호출해야 합니다.




    1
    2
    Thread thread = new Thread(Runnable r);
     
    cs

      


      Runnable은 인터페이스 타입이기 때문에 구현 객체를 만들어 대입해야 합니다. Runnable에는 run() 메소드가 정의되어 있는데, 구현 클래스는 run()을 재정의하여 작업 스레드가 실행할 코드를 작성해야 합니다. 다음은 Runnable 구현 클래스를 작성하는 방법을 보여줍니다.




    1
    2
    3
    4
    5
    class Task implements Runnable {
        public void run() {
            // Code 
        }
    }
    cs





      여기서 주의할 점은 Runnable은 작업 내용만 가지고 있는 객체입니다. 실제 스레드가 아니기 때문에 Runnable 구현 객체를 파라미터로 하는 Thread 생성자로 호출하여야만 작업 스레드가 생성되는 것 입니다.




    1
    2
    3
    Runnable task = new Task();
     
    Thread thread = new Thread(task);
    cs




      아니면 익명 구현 객체를 파라미터로 사용하여 코드를 절약할 수 있습니다.



    1
    2
    3
    4
    5
    6
    Thread thread = new Thread(new Runnable() {
        public void run() {
            // Code
        }
    });
     
    cs







      Runnable 인터페이스는 run() 메소드 하나만 정의되어 있기 때문에 함수적 인터페이스입니다. 즉 람다식을 파라미터로 사용할 수도 있습니다. 



    1
    2
    3
    4
    Thread thread = new Thread( () -> {
        // Code
    });
     
    cs






      위 세 가지 방법으로 작업 스레드를 생성하고 나면 start() 메소드로 다음과 같이 호출해야만 실행이 됩니다.



    1
    thread.start();
    cs






      실제로 예제를 살펴보겠습니다. 0.5초 주기로 beep 음을 발생하면서 동시에 프린팅 작업을 한다고 가정해 봅시다.


      두 작업은 서로 다른 작업이기에 스레드를 분리해서 작성해야 합니다. 먼저 첫 예제를 살펴봅시다.






     * BeepPrintExam.java


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package mT;
     
    import java.awt.Toolkit;
     
    public class BeepPrintExam {
        public static void main(String[] args) {
            Toolkit toolkit = Toolkit.getDefaultToolkit(); // Toolkit 객체 생성
            
            for (int i = 0; i < 5; i++) {
                toolkit.beep();
                try { Thread.sleep(500); } catch(Exception e) {}
                // 0.5 초 간격
            }
            
            for (int i = 0; i < 5; i++) {
                System.out.println("Print" + i);
                try { Thread.sleep(500); } catch(Exception e) {}
            }
        }
     
    }
     
    cs




      실행 결과 Beep 음이 발생하고 나서, Print가 찍히는 걸 확인 할 수 있습니다. 원하는 건 Beep음과 출력이 동시에 이루어져야 하는데 말입니다.


      beep 음을 발생시키면서 동시에 프린팅을 하려면 두 작업 중 하나를 메인 스레드가 아닌 다른 스레드에서 실행시켜야 합니다. 프린팅은 메인 스레드가 담당하고 비프음을 들려주는 것은 작업 스레드가 담당하도록 수정해봅시다.






     * BeepWork.java


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    package mT;
     
    import java.awt.Toolkit;
     
    public class BeepWork implements Runnable {
        @Override
        public void run() {
            Toolkit toolkit = Toolkit.getDefaultToolkit();
            
            for (int i = 0; i < 5; i++) {
                toolkit.beep();
                try { Thread.sleep(500); } catch (Exception e) {}
            }
        }
     
    }
     
    cs





     * BeepPrintExam2.java


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    package mT;
     
    public class BeepPrintExam2 {
        public static void main(String[] args) {
            Runnable beepWork = new BeepWork();
            Thread thread = new Thread(beepWork);
            thread.start();
            
            for (int i = 0; i < 5; i++) {
                System.out.println("Print" + i);
                try { Thread.sleep(500); } catch(Exception e) {}
            }
        }
     
    }
     
    cs







      위 방법도 있지만 익명 객체, 람다식을 통한 작성법도 있습니다. 아래 글 상자를 통해 살펴보세요.











     Thread 하위 클래스로부터 생성

      작업 스레드가 실행할 작업을 Runnable로 만들지 않고, Thread의 하위 클래스로 작업 스레드를 정의하면서 작업 내용을 포함시킬 수도 있습니다. 다음은 작업 스레드 클래스를 정의하는 방법인데, Thread 클래스를 상속한 후 run 메소드를 재정의(override)해서 실행할 코드를 작성하면 됩니다.



    1
    2
    3
    4
    5
    6
    public void workingThread extends Thread {
        @Override 
        public void run() {
            // Code
        }
    }
    cs





     위 방법으로 위와 같은 예제를 보도록 하겠습니다.




     * BeepThread.java


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    package mT;
     
    import java.awt.Toolkit;
     
    public class BeepThread extends Thread {
        @Override
        public void run() {
            Toolkit toolkit = Toolkit.getDefaultToolkit();
            
            for (int i = 0; i < 5; i++) {
                toolkit.beep();
                try { Thread.sleep(500); } catch (Exception e) {}
            }
        }
    }
     
    cs






     * BeepPrintExam3.java


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    package mT;
     
    public class BeepPrintExam3 {
        public static void main(String[] args) {
            Thread thread = new BeepThread();
            thread.start();
            
            for (int i = 0; i < 5; i++) {
                System.out.println("Print" + i);
                try { Thread.sleep(500); } catch(Exception e) {}
            }
        }
     
    }
     
    cs





     같은 방법으로 Thread 익명 객체를 생성하여 사용할 수도 있습니다.








     스레드 이름

      스레드는 자신만의 이름을 갖고 있습니다. 스레드의 이름은 디버깅할 때 어떤 스레드가 어떤 작업을 하는지 조사할 목적으로 사용되기도 합니다.


      메인 스레드는 "main"이라는 이름을 가지고 있고, 직접 생성한 스레드는 자동적으로 "Thread-n" 형식으로 설정됩니다. 


      직접 스레드 이름을 설정하고 싶다면 아래와 같이 setName() 메소드로 변경하면 됩니다.


      스레드 이름을 알고싶은 경우 getName() 메소드를 호출하면 됩니다.



    1
    2
    3
    Thread.setName("ThreadName");
     
    Thread.getName();
    cs




      setName()과 getName()은 Thread의 인스턴트 메소드이므로 스레드 객체의 참조가 필요합니다. 만약 스레드 객체의 참조를 갖고 있지 않다면, Thread의 정적 메소드인 currentThread()로 코드를 실행하는 현재 스레드의 참조를 알 수 있습니다.



    1
    Thread thread = Thread.currentThread();
    cs





      다음 예제는 메인 스레드의 참조를 얻어 스레드 이름을 콘솔에 출력하고, 새로 생성한 스레드의 이름을 setName() 메소드로 설정한 후, getName() 메소드로 읽는 예제입니다.





     * ThreadNameExam.java


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package mT;
     
    public class ThreadNameExam {
        public static void main(String[] args) {
            Thread mainThread = Thread.currentThread();
            System.out.println("시작 스레드 이름: " + mainThread.getName());
     
            Thread threadA = new ThreadA();
            System.out.println("작업 스레드 이름: " + threadA.getName());
            threadA.start();
     
            Thread threadB = new ThreadB();
            System.out.println("작업 스레드 이름: " + threadB.getName());
            threadB.start();
        }
     
    }
     
    cs




     * ThreadA.java


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    package mT;
     
    public class ThreadA extends Thread {
        public ThreadA() {
            setName("ThreadA");
        }
     
        @Override
        public void run() {
            for (int i = 0; i < 2; i++) {
                System.out.println(getName() + "가 출력한 내용");
            }
        }
    }
     
    cs




     * ThreadB.java


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    package mT;
     
    public class ThreadB extends Thread {
        public ThreadB() {
            setName("ThreadB");
        }
     
        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println(getName() + "가 출력한 내용");
            }
        }
    }
     
    cs




















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

    댓글

Designed by Tistory.