우리가 프로그램을 만들어서 그것을 실행할 때, 이것이 실행되는 것 자체를 프로세스라고 합니다. 그리고 프로세스는 최소한 하나의 쓰레드 (Thread)를 갖고 있습니다.


프로세스 자체는 직렬적인 성격을 갖고 있습니다. 즉 프로세스는 순서대로 프로그램을 처리하는데요, 경우에 따라 어떤 두가지 일을 동시에 진행하여야 하는 경우가 있을 수 있습니다. 이 사례는 아래의 코드를 통해 설명하도록 하겠습니다. 아무튼 이렇게 일을 분리하여 동시에 처리하기 위해 도입된 개념을 바로 쓰레드라고 합니다.


자바에서 쓰레드를 구현하는 방법은 크게 두가지가 있습니다.


1. Thread 클래스를 상속하여 구현

2. Runnable 인터페이스를 구현


두가지 쓰레드가 0으로 초기화 된 한 변수를 공유하면서 첫번째 쓰레드는 0.1초마다 그 변수에 1씩 더하는 역할을 총 1000번 하고 두번째 쓰레드는 0.11초마다 1을 빼는 역할을 총 1000회 하는 코드를 만들어 보겠습니다. 따라서 다음과 같이 세가지 클래스를 만듭니다.


(1) 정적 변수 값을 하나 보유한 Val 클래스

1
2
3
4
public class Val {
    public static int val = 0;
}
 
cs

(2) 0.1초마다 Val 클래스의 정적 변수에 1씩 더하는 역할을 총 100번 하는 클래스. 이 클래스는 Thread 클래스를 상속하여 만들어 보겠습니다. 코드에서 보시다시피 실행이 되고자 하는 내용은 run() 메소드를 오버라이드 하여 그 안에 작성을 해야 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 
public class Add extends Thread {
    
    @Override
    public void run() {
 
        // for문 내부의 명령을 100번 실행한다는 의미입니다.
        for(int i=0; i<100; i++) {
            // Val 클래스의 val이라는 정적 변수에 1 더합니다.
            Val.val++;
            
            // 그리고 변한 값을 콘솔에 보여줍니다.
            System.out.println("Value of val: " + Val.val);
            
            try {
                // sleep메소드는 일정 시간 동안 메소드의 실행을 일시적으로 중지합니다.
                // 여기서 인자는 1000분의 1초값이므로 0.1초는 100이 됩니다.
                Thread.sleep(100);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
cs

(3) 0.11초마다 Val 클래스의 정적 변수에서 1씩 빼는 역할을 총 100번 하는 클래스. 쓰레드 구현의 또다른 방법으로 이번에는 Runnable 인터페이스를 구현하여 만들어 보았습니다. 이 경우에도 run() 메소드를 오버라이드 하면서 그 안에 실행되고자 하는 내용을 입력합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
public class Subtract implements Runnable {
 
    @Override
    public void run() {
        
        for(int i=0; i<100; i++) {
            Val.val--;
            System.out.println("Value of val: " + Val.val);
            try {
                Thread.sleep(110);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
 
}
cs

(4) 위의 두가지 쓰레드를 실행하는 메인 메소드를 담은 테스트 클래스.

1
2
3
4
5
6
7
8
9
10
11
 
public class ThreadTest {
    
    public static void main(String args[]) {
        Add add = new Add();
        Subtract subtract = new Subtract();
        
        add.start();
        subtract.run();
    }
}
cs


코드에서 보면, 한가지 눈에 들어오는 차이점이 있습니다. Thread 클래스를 상속하여 만든 add 인스턴스를 실행하기 위해서는 start() 메소드를 실행하고, Runnable 인터페이스를 구현하여 만든 subtract 인스턴스를 실행하는 곳에서는 run() 메소드를 실행한다는 것입니다. 왜 그럴까요?


기본적으로 Thread 클래스는 start() 메소드를 호출함으로써 run() 메소드가 실행되도록 되어 있습니다. 비록 위의 add 인스턴스에서 run() 메소드를 직접 호출할 수도 있지만, 그 경우에 결과는 기대했던 것과는 전혀 다르게 나오게 됩니다. 프로그램이 순차적으로 실행되어 add 인스턴스가 갖고 있는 프로그램이 먼저 실행이 되고 이것이 다 끝난 뒤에야 나중에 나오는 subtract 인스턴스가 실행되는 것입니다. 


따라서 앞으로 쓰레드를 활용하여 프로그램을 만들 때, Thread 클래스를 상속한 쓰레드는 run() 메소드를 오버라이드 하여 내용을 채우고 이를 실행할 때는 start() 메소드를 호출한다는 점과 Runnable 인터페이스를 구현한 쓰레드는 run() 메소드 자체를 오버라이드 하여 호출한다는 점을 기억해야 할 것입니다.

반응형

'Java > 스레드' 카테고리의 다른 글

[자바] ExecutorService 클래스 소개  (0) 2017.04.24

+ Recent posts