참조 : http://blog.eairship.kr/126(금지된 엑시노아의 비공정 블로그)

스레드란 무엇인가?

"프로그램의 실행 흐름, 프로그램을 구성하고 있는 실행 단위"라 말할 수 있다. 보통 메인 문에서 순차적으로 실행되는 루틴을 많이 다뤄왔는데 이 것은 싱글 스레드이다. 만약에 하나의 프로그램에서 여러 개의 작업을 동시에 수행하고 싶을 경우에는 멀티 쓰레드(Multi Thread)를 사용하면 된다.


스레드를 어떻게 사용하는지?, 어떻게 동작하는지?

(1) Thread 클래스 상속하는 방법

public class ThreadTest {
    
    public static class MultiThread extends Thread{
        private String name;
        
        public MultiThread(String name){
            System.out.println(getName() + "스레드가 생성되었습니다.");
            this.name = name;
        }
 
        @Override
        public void run() {
            for(int i = 0;i<10;i++){
                System.out.println(getName() + "(" + this.name + ")");
                try {
                    sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    public static void main(String[] args) {
        MultiThread mt1 = new MultiThread("Thread1");
        MultiThread mt2 = new MultiThread("Thread2");
        MultiThread mt3 = new MultiThread("Thread3");
        
        mt1.start();
        mt2.start();
        mt3.start();
    }
}
cs
위의 코드는 Thread 클래스를 extends(상속)한 클래스에서 run()메소드를 정의하고 실행하는 코드입니다.

보통의 싱글 스레드에서 mt1.start(); 코드가 끝나고 mt2.start()코드가 실행되고, 그 다음 mt3.start()가 실행되지만 멀티 스레드에서는 위의 코드들이 동시에 수행됩니다. 그래서 아래의 결과 값처럼 순서에 상관없이 동시에 수행되는 것입니다.

Thread-0스레드가 생성되었습니다.
Thread-1스레드가 생성되었습니다.
Thread-2스레드가 생성되었습니다.
Thread-1(Thread2)
Thread-2(Thread3)
Thread-0(Thread1)
Thread-0(Thread1)
Thread-2(Thread3)
Thread-1(Thread2)
Thread-2(Thread3)
Thread-1(Thread2)
Thread-0(Thread1)
Thread-0(Thread1)
Thread-1(Thread2)
Thread-2(Thread3)
Thread-1(Thread2)
Thread-2(Thread3)
Thread-0(Thread1)
Thread-1(Thread2)
Thread-2(Thread3)
Thread-0(Thread1)
Thread-1(Thread2)
Thread-0(Thread1)
Thread-2(Thread3)
Thread-2(Thread3)
Thread-0(Thread1)
Thread-1(Thread2)
Thread-2(Thread3)
Thread-1(Thread2)
Thread-0(Thread1)
Thread-0(Thread1)
Thread-2(Thread3)
Thread-1(Thread2)
cs


Thread State

스레드의 상태는 다음의 형태로 변화됩니다.

Thread.State 값 

  설명 

NEW

 시작되지 않은 상태

RUNNABLE

 실행 가능 상태

WAITING 

 대기 상태

TIMED_WAITING

 스레드가 특정 시간동안 대기 상태

BLOCKED 

 스레드가 잠겨 있어 풀리기를 기다리는 상태

TERMINATED 

 스레드가 종료된 상태


(2) Runnable Interface를 이용하는 방법

public class ThreadTest {
    
    public static class MultiThread implements Runnable{
        private String name;
        
        public MultiThread(String name){
            System.out.println(name + "스레드가 생성되었습니다.");
            this.name = name;
        }
 
        @Override
        public void run() {
            for(int i = 0;i<10;i++){
                System.out.println(Thread.currentThread().getName() + "(" + this.name + ")");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    public static void main(String[] args) {
        MultiThread mt1 = new MultiThread("Thread1");
        MultiThread mt2 = new MultiThread("Thread2");
        MultiThread mt3 = new MultiThread("Thread3");
        Thread tr1 = new Thread(mt1);
        Thread tr2 = new Thread(mt2);
        Thread tr3 = new Thread(mt3);
        
        tr1.start();
        tr2.start();
        tr3.start();
    }
}
cs

결과값은 동일!!
Runnable Interface를 사용하여 스레드를 구현하는 이유는 무엇일까? 
Java라는 언어의 특성 상 다중 상속이 되지 않는다. 하지만 인터페이스는 다중 상속이 된다. Thread클래스를 상속 받으면 다른 클래스를 상속받지 못하게 된다.
그래서 실제로 쓰레드를 사용할 때는 Runnable Interface를 주로 사용한다.


Thread의 우선순위

public class ThreadTest {
    
    public static class MultiThread implements Runnable{
        private String name;
        
        public MultiThread(String name){
            System.out.println(name + "스레드가 생성되었습니다.");
            this.name = name;
        }
 
        @Override
        public void run() {
            for(int i = 0;i<10;i++){
                System.out.println(Thread.currentThread().getName() + "(우선순위 : " + Thread.currentThread().getPriority() + ")");
            }
        }
    }
    
    public static void main(String[] args) {
        MultiThread mt1 = new MultiThread("Thread1");
        MultiThread mt2 = new MultiThread("Thread2");
        MultiThread mt3 = new MultiThread("Thread3");
        Thread tr1 = new Thread(mt1);
        Thread tr2 = new Thread(mt2);
        Thread tr3 = new Thread(mt3);
        
        tr1.setPriority(Thread.MIN_PRIORITY);
        tr2.setPriority(Thread.NORM_PRIORITY);
        tr3.setPriority(Thread.MAX_PRIORITY);
        
        tr1.start();
        tr2.start();
        tr3.start();
    }
}
cs

쓰레드는 우선 순위에 따라 실행을 우선시 합니다. 물론 출력해보면 꼭 예상되로 되지는 않더라구요.

즉, 우선 순위가 높은 스레드는 우선 순위가 자기보다 낮은 스레드보다 더 많이 실행되는 것입니다.

만약 동일한 우선순위의 스레드가 둘 이상 존재할 경우 CPU의 할당시간을 분배후 실행합니다. 가장 높은 우선 순위는 10, 

기본 우선 순위는 5, 가장 낮은 우선 순위는 1로 , 보통 스레드의 우선 순위는 5입니다. 스레드의 우선 순위를 변경하고 싶다면

setPriority()메소드를 사용하면 됩니다. 위의 코드가 그 예제입니다. 


Thread의 동기화

public class ThreadTest {
    
    public static class clsNumber{
        int num = 0;
        public void addNum(){
            num++;
        }
        public int getNum(){
            return num;
        }
    }
    
    public static class MultiThread implements Runnable{
        clsNumber number;
        
        public MultiThread(clsNumber cn){
            number = cn;
        }
 
        @Override
        public void run() {
            for(int i = 0;i<10000;i++){
                number.addNum();
            }
        }
    }
    
    public static void main(String[] args) {
        
        clsNumber number = new clsNumber();
        
        MultiThread mt1 = new MultiThread(number);
        MultiThread mt2 = new MultiThread(number);
        MultiThread mt3 = new MultiThread(number);
        Thread tr1 = new Thread(mt1);
        Thread tr2 = new Thread(mt2);
        Thread tr3 = new Thread(mt3);
        
        tr1.start();
        tr2.start();
        tr3.start();
        
        try {
            // join()메소드는 스레드가 끝날 때까지 기다린다.
            tr1.join();
            tr2.join();
            tr3.join();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        System.out.println(number.getNum());
    }
}
cs

결과는??

15380


결과는 30000이 나와야 되는데 15380이 나옵니다. 물론 매번 값도 다르고요. 이유는 무엇일까요?

동기화가 되지 않기 때문에 수행 도중 실행 권한이 다른 스레드로 넘어가서 제대로 된 결과값을 도출해 낼 수 없게 되는 것입니다. 동기화를 해주어야 합니다. 자바에서는 synchronized키워드를 통해 동기화를 가능하게 해줍니다.


동기화를 해준 코드

다음과 같이 synchronized 키워드를 addNum()메소드에 붙여주면 동기화를 지원해주게 됩니다.

public class ThreadTest {
    
    public static class clsNumber{
        int num = 0;
        public synchronized void addNum(){
            num++;
        }
        public int getNum(){
            return num;
        }
    }
    
    public static class MultiThread implements Runnable{
        clsNumber number;
        
        public MultiThread(clsNumber cn){
            number = cn;
        }
 
        @Override
        public void run() {
            for(int i = 0;i<10000;i++){
                number.addNum();
            }
        }
    }
    
    public static void main(String[] args) {
        
        clsNumber number = new clsNumber();
        
        MultiThread mt1 = new MultiThread(number);
        MultiThread mt2 = new MultiThread(number);
        MultiThread mt3 = new MultiThread(number);
        Thread tr1 = new Thread(mt1);
        Thread tr2 = new Thread(mt2);
        Thread tr3 = new Thread(mt3);
        
        tr1.start();
        tr2.start();
        tr3.start();
        
        try {
            // join()메소드는 스레드가 끝날 때까지 기다린다.
            tr1.join();
            tr2.join();
            tr3.join();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        System.out.println(number.getNum());
    }
}
cs
결과는??
30000




'JAVA' 카테고리의 다른 글

mutable vs immutable  (0) 2015.11.27
자바 예외 처리  (0) 2015.11.21
GC(Garbage Collection)의 대상은 누구인가?  (0) 2015.11.10
LinkedHashMap, TreeMap, HashMap  (0) 2015.11.07
Comparable 인터페이스에 대한 이해  (0) 2015.11.07
Posted by slender ankles
,