Runnable 인터페이스와 Thread 클래스

스레드 생성 방법은 크게 2가지가 있다.

 

Runnable 인터페이스 사용

 

Runnable (Java Platform SE 8 )

The Runnable interface should be implemented by any class whose instances are intended to be executed by a thread. The class must define a method of no arguments called run. This interface is designed to provide a common protocol for objects that wish to e

docs.oracle.com

@FunctionalInterface
public interface Runnable {
	public abstract void run(); // 스레드가 시작되면 수행되는 메서드
}

 

Thread 클래스 사용

 

Thread (Java Platform SE 8 )

Allocates a new Thread object so that it has target as its run object, has the specified name as its name, and belongs to the thread group referred to by group, and has the specified stack size. This constructor is identical to Thread(ThreadGroup,Runnable,

docs.oracle.com

아래 주요 메서드에 대한 설명까지 적어뒀으므로 관심이 있다면 참고 바란다.

public class Thread implements Runnable
{
    // 생략

    public synchronized void start() {
    /**
      * 이 메서드는 main 메서드 스레드나 VM에 의해 만들어진 시스템 그룹 스레드에서 실행되는게 아니다.
      * 새로운 호출 스택을 만들고 JVM에 의해 관리된다.
      * 아래 threadStatus 변수 값이 0이면 NEW 상태를 나타낸다.
      * 이미 NEW 상태가 아니라는 것은 JVM에 의해 관리되고 있는 스레드라는 뜻이므로
      * IllegalThreadStateException 예외로 처리한다.
      */
      if (threadStatus != 0)
          throw new IllegalThreadStateException();

      // 스레드를 스레드 그룹에서 관리해주기 위해 추가한다.
      group.add(this);

      boolean started = false;
      try {
          start0();
          started = true;
      } finally {
          try {
              if (!started) {
                  group.threadStartFailed(this);
              }
          } catch (Throwable ignore) {
            /* 아무것도 안한다. 만약 start0() 메서드가 예외를 던졌다면
            메모리의 메서드 콜 스택으로 전달 될것이다. */
          }
      }
    }

      void add(Thread t) {
        synchronized (this) {
          if (destroyed) {
              throw new IllegalThreadStateException();
          }
          if (threads == null) {
              threads = new Thread[4];
          } else if (nthreads == threads.length) {
          	  // 스레드를 동적 할당하는 부분이다.
              threads = Arrays.copyOf(threads, nthreads * 2);
          }
          threads[nthreads] = t;
          nthreads++;

          // 스레드는 아직 시작되거나 시작되지 않았더라도
          // 이제 스레드 그룹에 완전히 속하게 됐다.
          // 이 때문에 스레드 그룹이 파괴되지 않아서 시작되지 않은 스레드 수가 줄어든다.
          nUnstartedThreads--;
      }
    }

    private native void start0();

    @Override
    public void run() {
      if (target != null) {
          target.run();
      }
    }

    // 생략
}

 

Thread 클래스는 Runnable 인터페이스를 구현한 것이다.


Thread 클래스의 메서드

sleep()

매개변수로 넘어온 시간 (밀리초, 나노초 단위) 만큼 스레드를 대기시킨다.

 

sleep()을 사용할 때 try-catch로 묶어줘야만 한다. 그리고 적어도 InterruptedException으로 catch 해야 한다.

왜냐하면 sleep()가 적어도 InterruptedException 던질 수도 있다고 선언돼있기 때문이다.

다만 InterruptedException를 catch 해봤자 해줄 수 있는 것이라곤 RuntimeException으로 감싸서 다시 던지는 정도 밖에는 없다.

join()

https://www.javaquery.com/2016/08/understanding-threadjoin-in-java.html

join() 파라미터는 ms 단위로 해당 스레드가 타깃 스레드를 위해 얼마나 기다릴지를 정해주는 값을 나타낸다.

기본값은 0이며 이는 계속 기다리겠다는 의미이고 특정 시간 값을 넣어주면 그 시간만큼만 기다린다.

 

sleep()과 마찬가지로 try-catch 처리를 해줘야 한다.

 

start()

스레드 시작 메서드

 

NEW 상태에 있는 스레드를 RUNNABLE 상태로 만들어주기 위해 스레드 객체 정보를 스레드 대기열 큐에 넣어준다.

https://codingdog.tistory.com/entry/java-thread-start-vs-run-어떤-차이가-있을까요

스레드가 작업을 실행할 호출 스택을 만들고 그 안에 run() 메서드를 올려준다.

https://wisdom-and-record.tistory.com/48

 

checkAccess()

현재 수행 중인 스레드가 해당 스레드를 수정할 수 있는 권한이 있는지를 확인한다.

만약 권한이 없으면 SecurityException 예외가 발생한다.

 

isAlive()

해당 스레드의 run() 메서드가 종료됐는지에 따라 살아있는지 죽었는지 확인한다.

 

isInterrupted()

run() 메서드가 정상적으로 종료 안되고 interrupt() 메서드의 호출을 통해서 종료됐는지 확인한다. 다른 스레드에서 확인할 때 사용한다.

 

interrupted()

스레드 본인이 현재 스레드가 중지됐는지 확인한다.

 

currentThread()

현재 수행 중인 스레드의 객체를 리턴한다.

 

dumpStack()

콘솔 창에 현재 스레드 스택 정보를 출력한다.

 

💡 참고로 Thread, Runnable 둘 다 java.lang 패키지에 있어서 별도로 import 할 필요는 없다.

 

💡 resume(), stop(), suspend()는 스레드를 교착상태로 만들기 쉽기 때문에 deprecated 되었다.

 


Object 클래스의 스레드 관련 메서드

wait(), wait(long timeout), wait(long timeout, int nanos)

다른 스레드가 Object 객체에 대한 notify() 메서드나 notifyAll() 메서드를 호출할 때까지 현재 스레드가 대기하고 있도록 한다.

timeout 파라미터는 지정한 밀리초 단위의 시간만큼 대기하며,
nanos 파라미터는 나노초 단위의 시간만큼 대기하는 것이다.

 

notify()

Object 객체의 모니터에 대기(Waiting 상태)하고 있는 단일 스레드를 깨운다.(Runnable 상태)

 

notifyAll()

Object 객체의 모니터에 대기하고 있는 모든 스레드를 깨운다.

 


스레드 구현 클래스 'Thread vs Runnable'선택 기준

Thread 클래스를 상속받으면 다른 클래스를 상속받을 수 없기 때문에 이 경우에는 Runnable 인터페이스를 구현하는 것이 일반적이다. Runnable 인터페이스를 구현하는 방법은 재사용성이 높고 코드의 일관성을 유지할 수 있다는 장점이 있기 때문에 보다 객체지향적인 방법이라 할 수 있다. 대신 필요할 때 Thread 객체를 생성해서 사용해야 한다.

 

결국 Thread 클래스가 다른 클래스를 확장할 필요가 있을 경우나 익명 클래스로 한 곳에서만 스레드를 사용하는 경우는 Runnable 인터페이스를 구현하고, 아니면 Thread 클래스 사용하는 게 편하다.

 


출처

Thread 클래스

docs.oracle.com/javase/8/docs/api/java/lang/Thread.html

 

Runnable 인터페이스

docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html

 

resume(), stop(), suspend() 스레드를 교착상태로 만들기 쉽기 때문에 Deprecated된 정보

docs.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html

 

스레드 메서드 정리

https://www.notion.so/ac23f351403741959ec248b00ea6870e