저번 시간에 이어서 이번에는 Runnable 인터페이스를 구현한 후 Thread 객체를 통해 실행하는 방법을 알아보도록 하겠습니다.

 


2.Runnable 인터페이스 구현을 통한 방법
제목에서처럼 Runnable은 인터페이스 입니다.
Runnable 인터페이스에는 run()이라는 추상메서드 하나만 존재합니다.
이 Runnable 인터페이스를 가지고 어떻게 다중상속을 이용하여 스레드를 실행 시킬까요?

Runnable.java
1
2
3
public interface Runnable {
    public abstract void run();
}
cs

 

자세한 실행 방법은 다음과 같습니다.
1. Runnable 인터페이스 구현
2. 구현한 Runnable 객체를 생성하여 Thread에게 전달
3. Thread start()

먼저 Runnable 인터페이스를 구현합니다.

MyRunnable.java
1
2
3
4
5
6
7
8
9
10
11
12
public class MyRunnable implements Runnable{
 
    @Override
    public void run() {
        for(int i = 0; i < 300; i++){
            System.out.println("MyRunnable 스레드");
        }
 
        System.out.println("MyRunnable 스레드 종료");
    }
 
}
cs

 

Thread(Runnable target) 생성자를 이용하여
구현한 Runnable 객체를 만들어 Thread에게 넘긴 후 스레드의 start() 메서드를 실행합니다.

 

MyThread_2 .java

1
2
3
4
5
6
7
8
9
10
11
12
public class MyThread_2 {
 
     public static void main(String[] args) {
           Runnable myRun = new MyRunnable();
 
           Thread thread = new Thread(myRun);
           thread.start();
 
           System.out.println("main스레드 종료");
     }
 
}
cs

 

 

또는 다음과 같이 익명객체로 Runnable 인터페이스를 구현하는 동시에 생성하여 Thread의 생성자로 넘길 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MyThread_2 {
 
     public static void main(String[] args) {
 
           Thread thread = new Thread(new Runnable() {
 
                @Override
                public void run() {
                     for(int i = 0; i < 300; i++){
                           System.out.println("MyRunnable 스레드");
                     }
 
                     System.out.println("MyRunnable 스레드 종료");
                }
           });//익명객체로 구현하여 Thread 생성자로 넘김
 
           thread.start();
 
           System.out.println("main스레드 종료");
     }
 
}
cs

 

 

 

결과

 

1
2
3
4
5
6
7
8
9
10
MyRunnable 스레드
main스레드 종료
MyRunnable 스레드
MyRunnable 스레드
MyRunnable 스레드
MyRunnable 스레드
MyRunnable 스레드
..생략..
MyRunnable 스레드 종료
 
cs

 

지난시간에 말씀드린 것처럼 결과는 스레드 스케줄러의 스케줄링에 따라 조금씩 달라질 수 있습니다.




왜 자바는 굳이 스레드 실행방법을 두 가지로 나누었을까요??
이유는 간단합니다. 인터페이스의 존재 이유중 하나이기도 하죠.
바로 다중 상속을 지원하기 위함입니다.

자바는 객체지향 언어로서 객체지향은 상속, 캡슐화, 추상화, 다형성 을 이용할 수 있고
이 특성들로 객체지향적인 코드 설계를 통해 코드의 중복성 제거를 하여 유지보수 및 생산성을 높일 수 있습니다.

객체지향적 설계 -> 코드의 중복성 제거 -> 유지보수 및 생산성 향상

갑자기 이 말을 왜 하는지 의아하실 겁니다.
만약 Thread 생성을 통한 방법만을 제공한다면?
그렇습니다. 스레드를 이용하기 위해서는 무조건 Thread를 상속받아야만 하고 그렇게 되면
스레드를 이용하기 위해서는 객체지향의 특성 중 하나인 상속을 포기해야 하기 때문이죠.


만약 사람 이라는 추상클래스가 있다고 하죠.
마린과 매딕은 사람 클래스를 상속받았고
사람에는 공통적인 부분을 구현 및 추상화 하였습니다.

 

 

 

(클래스 다이어그램은 대충 그렸으니 양해바람)


그리고 우리는 마린과 매딕을 각각 스레드로 만들어 따로 움직이게 하려 합니다.
이때 아까 말했던 것처럼 Thread 상속을 통한 생성만 가능하다면
마린과 매딕은 스레드가 되기 위해 어쩔 수 없이 이름,나이,몸무게, 먹고 싸고 움직이는것을 포기해야 합니다.

마린 매딕에 각각 멤버들을 만들어주면 되지 않나요?
그것은 코드가 중복되고 유지보수를 어렵게 만들어 생산성을 저하시키고 객체지향의 특성을 포기하는 것입니다.

또한 Runnable 인터페이스를 통해 규격을 제시할 수 있고
개발자는 해당 인터페이스를 통해 규격에 따라 구현만 하면 되는 것입니다.

이렇게 되면 다형성에 의해 Runnable을 구현한 클래스의 인스턴스는 Runnable 객체가 되는것입니다.




Runnable 인터페이스와 Thread는 무슨 관계죠?
스레드는 객체지향의 특성중 하나인 다형성을 잘 이용한 예중 하나입니다.

잠시 여기서 스레드 클래스를 보면 다음와 같이 되어있습니다.
※많은 코드들은 생략하고 중요한 부분만 요약된 코드입니다.

 

 

보시면 Thread 또한 Runnable 인터페이스를 구현했으므로 run() 메서드가 존재합니다.
이렇게 인터페이스를 두게 되면 틀을 제시할 수 있죠.

또한 생성자의 매개변수로 Runnable 인터페이를 두었으므로 다형성으로 인해
Runnable 인터페이스를 구현한 객체라면 그것이 마린이든 메딕이든 누구든지 스레드로 실행시킬 수 있습니다.
그대신 그 메딕이나 마린 또한 run() 메서드를 오버라이딩 해야 하죠.

Thread 객체 입장에서는 저 Runnable 구현 객체가 메딕이 들어오든 마린이 들어오든 신경쓸 필요가 없다는 것입니다.
이런것을 '결합도가 낮다' 라고 이야기하며 모듈화 되었다고 이야기 합니다.
Thread 객체에 메딕과 마린을 생성자에 매개변수로 던져주기만 하면 코드에 변경없이 갈아 끼울 수 있는 것이죠.

이렇게 Thread의 start() 를 호출하게되면 현재 스레드 스케줄러에게 등록되어
새로운 스레드로서 실행되게 되고 대기상태에 있다가 혹은 곧바로 스케줄러에 의해 실행되면서 run()메서드가 호출됩니다.

이해되시나요??
이렇게 스레드 실행 두 가지를 알아보았고 이후에는 스레드에서 실행할 수 있는 메서드와 스레드에 대한 더욱 깊은 개념을 알아보도록 하겠습니다.

 

 

이전글

[Java]스레드(Thread) - [2] 스레드 생성과 실행

 

 

블로그 이미지

도로락

IT, 프로그래밍, 컴퓨터 활용 정보 등을 위한 블로그

,