BackEnd/Java

Multi-Thread Programming

hanseom 2022. 1. 23. 03:13
반응형

  Concurrent Software란 동시에 여러 작업을 할 수 있는 Software를 말합니다. Java에서 지원하는 Concurrent Progmming은 멀티프로세싱 (ProcessBuilder)과 멀티쓰레드가 있습니다.

 

Multi-Thread Progmming 생성 방식

1. Thread 상속

public class MultiThread {

  public static void main(String[] args) {
    // 코드 상으로는 Thread-0 출력 이후 main이 출력되어야 하지만, 멀티쓰레드는 동시에 수행되기에 출력 순서를 보장하지 않습니다.
    MyThread myThread = new MyThread();
    myThread.start();

    System.out.println(Thread.currentThread().getName()); // [결과]: main
  }

  static class MyThread extends Thread {
    @Override
    public void run() {
      System.out.println(Thread.currentThread().getName()); // [결과]: Thread-0
    }
  }

}

2. Runnable 구현

public class MultiThread {

  public static void main(String[] args) {
    // 코드 상으로는 Thread-0 출력 이후 main이 출력되어야 하지만, 멀티쓰레드는 동시에 수행되기에 출력 순서를 보장하지 않습니다.
    Thread thread = new Thread(() -> {
      System.out.println(Thread.currentThread().getName()); // [결과]: Thread-0
    });
    thread.start();

    System.out.println(Thread.currentThread().getName()); // [결과]: main
  }

}

 

Thread 주요 기능

  • sleep: 다른 쓰레드가 처리할 수 있도록 대기하지만 락을 잡고 있습니다. (Deadlock 발생 가능성 존재)
  • interrupt: interruptedException을 발생시켜 쓰레드를 깨웁니다.

  아래 코드는 thread가 3초 대기 후 종료되어야 하지만 main thread에서 interrupt를 발생시켜 1초 후 종료됩니다.

public class MultiThread {

  public static void main(String[] args) throws InterruptedException {
    // 코드 상으로는 Thread-0 출력 이후 main이 출력되어야 하지만, 멀티쓰레드는 동시에 수행되기에 출력 순서를 보장하지 않습니다.
    Thread thread = new Thread(() -> {
      System.out.println(Thread.currentThread().getName()); // [결과]: Thread-0
      try {
        Thread.sleep(3000L); // 3초간 sleep
      } catch (InterruptedException e) {
        System.out.println("Interrupted!");
        return; // Exit
      }
    });
    thread.start();

    System.out.println(Thread.currentThread().getName()); // [결과]: main
    Thread.sleep(1000L); // 1초간 sleep
    thread.interrupt(); // thread의 interruptedException을 발생시킵니다.
  }

}
  • join: 다른 쓰레드가 끝날 때까지 기다립니다.

  아래 코드에서 thread.join(); 생략 시 "Finished!"는 바로 출력되지만, thread.join(); 호출 시에는 thread 수행(3초)이 종료된 후 "Finished!"가 출력됩니다.

public class MultiThread {

  public static void main(String[] args) throws InterruptedException {
    // 코드 상으로는 Thread-0 출력 이후 main이 출력되어야 하지만, 멀티쓰레드는 동시에 수행되기에 출력 순서를 보장하지 않습니다.
    Thread thread = new Thread(() -> {
      System.out.println(Thread.currentThread().getName()); // [결과]: Thread-0
      try {
        Thread.sleep(3000L); // 3초간 sleep
      } catch (InterruptedException e) {
        System.out.println("Interrupted!");
        return; // Exit
      }
    });
    thread.start();

    System.out.println(Thread.currentThread().getName()); // [결과]: main

    thread.join(); // thread가 끝날 때까지 기다립니다.
    System.out.println("Finished!");
  }

}

 


 

Executors

  High-Level Concurrency 프로그래밍으로 쓰레드를 만들고 관리하는 작업을 애플리케이션에서 분리하고, 그런 기능을 Executors에게 위임합니다. Executors는 애플리케이션이 사용할 쓰레드 풀을 만들고, 쓰레드 생명 주기를 관리합니다. 쓰레드로 실행할 작업을 제공할 수 있는 API를 제공합니다.

 

주요 인터페이스

  • Executor: execute(Runnable)
  • ExecutorService: Executor 상속 받은 인터페이스로 Callable도 실행할 수 있으며, Executor를 종료시키거나 여러 Callable을 동시에 실행하는 등의 기능을 제공합니다.
  • ScheduledExecutorService: ExecutorService를 상속받은 인터페이스로 주기적으로 작업을 실행할 수 있습니다.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class MultiThread {

  public static void main(String[] args) {
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    executorService.submit(() -> System.out.println(Thread.currentThread().getName()));
    // [결과]: pool-1-thread-1

    // executorService는 프로세스가 죽지 않기 때문에 명시적으로 종료시켜 주어야 합니다.
    executorService.shutdown(); // 처리중인 작업 완료후 종료
    // executorService.shutdownNow(); // 즉시 종료

    ExecutorService fixedExecutorService = Executors.newFixedThreadPool(2);
    fixedExecutorService.submit(getRunnable());
    fixedExecutorService.submit(getRunnable());
    fixedExecutorService.submit(getRunnable());
    fixedExecutorService.shutdown();
    // [결과]: thread-1 과 thread-2 동시 처리
    // pool-2-thread-2
    // pool-2-thread-1
    // pool-2-thread-1

    ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
    scheduledExecutorService.schedule(getRunnable(), 3, TimeUnit.SECONDS);
    scheduledExecutorService.shutdown();
    // 종료 없이 주기적으로 무한히 수행하는 방식입니다.
    // scheduledExecutorService.scheduleAtFixedRate(getRunnable(), 1, 2, TimeUnit.SECONDS);
  }

  private static Runnable getRunnable() {
    return () -> {
      System.out.println(Thread.currentThread().getName());
    };
  }

}
반응형