Thread와 Runnable

wannabeking·2022년 4월 24일
0

Java

목록 보기
1/13
post-thumbnail

Runnable

  • Runnable은 파라미터도 없고 리턴 값도 없는 함수형 인터페이스이다. -> run()
@FunctionalInterface
public interface Runnable {

    public abstract void run();
}
  • Runnable을 구현하여 Thread의 생성자 파라미터로 사용한다.
  • 동일 Runnable 인스턴스로 생성한 Thread들은 병렬적이지 않고 직렬적이다.
class MyRunnable implements Runnable{

    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        String threadGroup = Thread.currentThread().getThreadGroup().getName();
        for(int i = 0; i < 5; i++) {
            System.out.println("[" + threadGroup + "] " + threadName + ": " + i);
        }
    }
}

Runnable r1 = new MyRunnable();
Runnable r2 = new MyRunnable();
Runnable r3 = new MyRunnable();

Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
Thread t3 = new Thread(r3);

t1.setName("t1");
t2.setName("t2");
t3.setName("t3");

t1.start();
t2.start();
t3.start();

[main] t2: 0
[main] t2: 1
[main] t3: 0
[main] t3: 1
[main] t3: 2
[main] t1: 0
[main] t1: 1
[main] t1: 2
[main] t1: 3
[main] t1: 4
[main] t3: 3
[main] t3: 4
[main] t2: 2
[main] t2: 3
[main] t2: 4


Process finished with exit code 0

  • 기본적으로 main 스레드 그룹에 속한다.


Thread Group

  • JVM이 실행되면 최상위인 system 스레드 그룹을 생성하며 다음과 같다.

    Name : Signal Dispatcher(데몬)
    소속그룹 : system
    Name : AutoSaveThread(데몬)
    소속그룹 : main
    Name : main(주)
    소속그룹 : main
    Name : Attach Listener(데몬)
    소속그룹 : system
    Name : Finalizer(데몬) - 가비지 컬렉터 담당.
    소속그룹 : system
    Name : Reference Handler(데몬)
    소속그룹 : system

  • main 스레드 그룹은 system 스레드 그룹의 하위 그룹이다.
  • 자바 어플리케이션의 시작인 main() 함수 호출은 main 스레드 그룹에 속한 main 스레드를 실행하는 것이다. 따라서 main 스레드 그룹에서 시작한다.
  • 스레드 그룹 생성자는 다음과 같다.
    • public ThreadGroup(String name)
    • public ThreadGroup(ThreadGroup parent, String name)
  • 스레드 그룹 생성 시 부모를 설정하지 않으면 현재 스레드 그룹의 하위 스레드 그룹이 된다.
  • 따라서 작업 시 생성하는 스레드 그룹들은 웬만하면 main 스레드 그룹에 속할 것이다.
  • 스레드 그룹에 일괄적으로 interrupt()가 가능하다.


Thread

  • ThreadRunnable을 implements 하여 run()이 존재한다.
  • 따라서 Thread 를 extends 하고 run()을 오버라이딩 해도 된다.
  • Threadstart()에 의하여 실행된다.
public synchronized void start() {
        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) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
  • start()를 하면 해당 스레드를 스레드 그룹에 추가한다.
  • Thread의 생성자는 다음과 같다.
    • public Thread()
    • public Thread(Runnable target)
    • public Thread(ThreadGroup group, Runnable target)
    • public Thread(String name)
    • public Thread(ThreadGroup group, String name)
    • public Thread(Runnable target, String name)
    • public Thread(ThreadGroup group, Runnable target, String name)
    • public Thread(ThreadGroup group, Runnable target, String name, long stackSize)
  • 생성자로 Runnable, name, ThreadGroup, stackSize 설정이 가능하다.
  • name은 getter, setter 존재 한다.
  • name 설정 안하면 Thread-0, Thread-1... 과 같이 증가한다.

class MyThread extends Thread{

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        String threadGroup = Thread.currentThread().getThreadGroup().getName();
        for(int i = 0; i < 5; i++) {
            System.out.println("[" + threadGroup + "] " + threadName + ": " + i);
        }
    }
}

Thread t1 = new MyThread("t1");
Thread t2 = new MyThread("t2");
Thread t3 = new MyThread("t3");

t1.start();
t2.start();
t3.start();

[main] t3: 0
[main] t3: 1
[main] t3: 2
[main] t3: 3
[main] t3: 4
[main] t1: 0
[main] t1: 1
[main] t1: 2
[main] t1: 3
[main] t1: 4
[main] t2: 0
[main] t2: 1
[main] t2: 2
[main] t2: 3
[main] t2: 4


Process finished with exit code 0

  • 위에 Runnable구현하여 생성자를 통해 넘기는 방법처럼 Thread를 상속 받아서 구현할 수 있다.
  • 무한히 실행되는 스레드를 종료하려면?
    • interrupt() 사용
      • while문 조건식에 isInterrupted() 아닐 때 까지 넣는다.
      • interrupt()하면 while문 빠져나오게 된다.
    • stop()은 안전하지 않아 Deprecated 되어 있음.
      • 메모리에 남아 뜻밖의 오류 발생할 수 있다.
  • Java는 단일 상속이기 때문에 Thread 상속받는거 보다는 Runnable을 구현하여 생성자로 넘기는 방법이 좋을 것 같다.


적절하게 사용하자

  • 멀티 스레드를 사용하는 이유?
    • 병렬적으로 일을 수행하여 일을 빠르게 처리하기 위하여 사용한다.
  • 사용해도 될 때?
    • 독립적이고 실행 순서가 상관없는 일에 사용해도 된다.
  • 조심해야 할 점은?
    • 교착상태에 빠질 수 있으니 독립적으로 수행할때만 사용하자.

      교착상태(Deadlock)란 둘 이상의 스레드가 서로의 작업이 끝나기만을 기다리며 작업을 더 이상 진행하지 못하는 상태를 의미한다.



profile
내일은 개발왕 😎

0개의 댓글