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());
};
}
}
반응형