본문 바로가기

04번. IT 힌트얻기/▶ 자바

스레드 만들고 시작하기(thread, runnable)

스레드를 만드는 방법과 그에 우선하여 왜 멀티 스레드가 필요한지 알아보자.
이를 가장 잘 설명할 수 있는 것이 바로 문서작성소프트웨어의 경우 인쇄를 하면서 동시에 문서 수정도 가능하다. 이런 것들은 모두 멀티 스레드로 돌려지기 때문에 가능한 것이다.

자바에서 스레드를 이용하려면, Tread 클래스의 객체를 인스턴스화(instantiate) 해야 하고, 이 때 스레드에서 실행되어야 하는 코드를 작성해 주어야 한다. 두 가지 방식이 있다.

(1) Runnable 인터페이스를 구현

자바에서 인터페이스는 클래스 보다 좀 더 추상적인 개념이다. 가령, 로봇에 대한 속성이나 동작 등을 클래스로 정의한다면, 어떻게 동작해야 하는 지 까지 세부 소스 코드를 작성해야 한다. 그러나, 인터페이스의 경우 어떠한 동작을 한다라고 까지만 명시할 뿐, 내부적으로 그 동작을 수행하기 위한 소스 코드를 작성할 필요가 없다.

Runnable 인터페이스는 run()이라는 메소드 하나만 달랑 가지고 있다. 그러므로 Runnable 인터페이스를 구현(Implements)한 클래스에서 run()메소드를 오버라이딩(overriding)하여 스레드가 동작하면 어떠한 작업을 수행할 것인지를 run()메소드 내에서 명시해 줘야 한다

그 사용 예는 다음과 같다.

public class HelloRunnable implements Runnable{

public void run(){
System.out.println("Hello from a thread!");
}

public static void main(String args[]){
            (new Thread(new HelloRannable())).start();
}
}



(2) Thread 클래스를 상속

Thread 클래스 자체는 Runnale 인터페이스를 구현했다. 물론, run()메소드는 개발자가 오버라이딩 하여 원하는 작업을 수행하도록 정의해야 한다. 아래는 그 예이다.

public class HelloThread extends Thread{

public void run(){
    System.out.println("Hello from a thrad!");
}

public static void main(String args[]){
    (new HelloThread()).start();
}

}

두 가지 예에서 공통점이 모두 start()를 호출하여 스레드를 시작한다는 점이다.

 
위 두 가지 방법 중 더 자주 쓰이는 방법은 1번의 방법이다. 자바는 C++ 처럼 다중 상속을 허용하지 않는다. 즉, extends class1, class2처럼 동시에 두 가지 클래스를 상속할 수 없으며, extends class1 이든 extends class2이든 한 번에 하나만 상속해야 한다. 그러므로 Thread 클래스를 상속하여 subclass를 만들 경우, 동시에 다른 클래스를 상속할 수 없는 단점이 있다. 그러므로 Runnable 인터페이스를 구현하는 방법이 더 많이 쓰인다.


(3) Thread.Sleep

스레드를 실행하다가 일정 시간 동안 실행을 멈춰야 할 경우가 있다. 이럴 경우 sleep 메소드를 사용한다. Thread.sleep 메소드는 현재 돌아가는 스레드가 해당 메소드의 파라미터로 전달되는 값의 밀리 초 만큼 실행을 멈추도록 한다. 그런데, 여기서 파라미터로 전달되는 값 대로 정확히 그 시간 만큼 실행을 멈춘다고 여겨서는 안된다. 이러한 한계가 있는 것은 내부의 OS가 제공하는 어떤 장치에 의해 제한되기 때문이다. 또한 sleep 시간 안에 있는데 인터럽트(Interrupt)가 발행하여 sleep이 종료될 수 있다. 이러한 상황(즉, 현재 스레드가 sleep 상태에 있는데 다른 스레드에서 인터럽트를 날려서 현재 스레드가 sleep이 깨어진 상황)에서는 현재 스레드가 InterruptedException을 발생시킨다.

public class TestThread {
public static void main(String args[]) throws InterruptedException{
Thread th1 =  new Thread(new Thread1());
Thread th2 = new Thread2()

th1.start();
th2.start();
}

private static class Thread1 implements Runnable{
@Override
public void run(){
         for(int i = 0; i<10; i++){
         System.out.println(Thread.currentThread().getName()+" : " + i);
              try {
                       Thread.sleep((int)(Math.random()*100));
             } catch (InterruptedException e) {
                    e.printStackTrace();
             }
}
}

private static class Thread2 extends Thread{
             @Override
             public void run(){
                      for(int i=0 ; i<10 ; i++){
                             System.out.println(getName() + " : " + i);
                             try {
                                    Thread.sleep((int)(Math.random()*100));
                            } catch (InterruptedException e) {
                                  e.printStackTrace();
                            }
                      }
              }
}
}