package lesson08;
class Ex06_MyThread extends Thread{
@Override
public void run() {
System.out.println("스레드 실행 중! - " + Thread.currentThread().getName());
}
}
public class Ex06_Main {
public static void main(String[] args) {
Ex06_MyThread t1 = new Ex06_MyThread();
t1.start();
}
}
package lesson08;
class Ex07_MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("스레드 실행 중! - " + Thread.currentThread().getName());
}
}
public class Ex07_Main {
public static void main(String[] args) {
Thread t1 = new Thread(new Ex07_MyRunnable());
t1.start();
}
}
package lesson08;
public class Ex08_Main {
public static void main(String[] args) {
Thread t1 = new Thread(){
@Override
public void run() {
System.out.println("익명 Thread 클래스 실행!");
}
};
t1.start();
}
}
package lesson08;
class Ex09_PrintTask implements Runnable{
private String message;
public Ex09_PrintTask(String message){
this.message = message;
}
@Override
public void run() {
for(int i = 0; i<5; i++){
System.out.println(message + " - " + i);
}
}
}
public class Ex09_Main {
public static void main(String[] args) {
Thread t1 = new Thread(new Ex09_PrintTask("김사과"));
Thread t2 = new Thread(new Ex09_PrintTask("반하나"));
Thread t3 = new Thread(new Ex09_PrintTask("오렌지"));
t1.start();
t2.start();
t3.start();
}
}
스레드 스케줄링은 OS가 결정하므로 매번 순서가 달라진다.
김사과 - 0
반하나 - 0
오렌지 - 0
김사과 - 1
오렌지 - 1
반하나 - 1
김사과 - 2
같은 숫자끼리 줄 맞춰 나오지 않는 이유 = context switching. CPU가 세 스레드를 번갈아 가며 실행하기 때문이다.
synchronized 키워드 : 한 번에 한 스레드만 메서드에 접근하도록 한다.class Counter {
private int count = 0;
public void increment() {
count++; // 동시에 접근 시 문제 발생 가능
}
public int getCount() {
return count;
}
}
public synchronized void methodName() {
// 이 메서드는 한 번에 하나의 스레드만 실행 가능
}
public void methodName() {
synchronized (this) {
// this: 현재 인스턴스를 lock
}
}
package lesson08;
class Ex10_Counter{
private int count = 0;
public synchronized void increment(){
count++;
}
public int getCount(){
return count;
}
}
public class Ex10_Main {
public static void main(String[] args) throws InterruptedException{
Ex10_Counter counter = new Ex10_Counter();
Runnable task = () -> {
for (int i = 0; i < 10000; i++){
counter.increment();
}
};
Thread th1 = new Thread(task);
Thread th2 = new Thread(task);
th1.start();
th2.start();
th1.join();
th2.join();
System.out.println("최종 카운트: " + counter.getCount());
}
}
Thread.join() 메서드 : 다른 스레드가 종료될 때까지 현재 스레드(보통 main 스레드)가 기다리게 만드는 메서드.
package lesson08;
import java.util.Stack;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Ex11_Main {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
// 스레드 풀에 스레드 3개를 생성
for(int i=1; i<=5; i++){
int taskId = i;
executor.submit(()->{
System.out.println("작업 " + taskId + "을 실행 중 (스레드: " + Thread.currentThread().getName() + ")");
try{
Thread.sleep(1000);
}catch(InterruptedException e){
System.out.println("작업 " + taskId + "완료");
}
});
}
executor.shutdown();
}
}
Runnable : 반환값이 없는 작업만 가능Callable<T> : 반환값을 가질 수 있는 작업을 표현Future<T> : Callable 작업의 결과를 나중에 비동기적으로 받아올 수 있는 객체package lesson08;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Ex12_Main {
public static void main(String[] args) throws Exception{
ExecutorService executor = Executors.newSingleThreadExecutor();
// 하나의 스레드만 사용하는 ExecutorService를 생성
Callable<Integer> task = () ->{
System.out.println("복잡한 계산 중...");
Thread.sleep(2000);
return 42;
};
Future<Integer> future = executor.submit(task);
System.out.println("메인 스레드는 다른 작업 중...");
Integer result = future.get();
System.out.println("계산 결과: " + result);
executor.shutdown();
}
}
submit() → Callable 작업을 실행하고 결과를 Future로 감싸 반환future.get() → 결과가 나올 때까지 대기여러 Callable<T> 작업을 한꺼번에 제출하고, 모든 작업이 완료될 때까지 대기한 뒤 각 작업의 결과를 List<Future<T>>로 반환한다.
get()을 호출하면 예외 없이 즉시 결과를 얻을 수 있다.(invokeAll(tasks, timeout, unit)).package lesson08;
import java.util.List;
import java.util.concurrent.*;
public class Ex13_Main {
public static void main(String[] args) throws InterruptedException{
ExecutorService executor = Executors.newFixedThreadPool(3);
List<Callable<String>> tasks = List.of(
()->{Thread.sleep(500); return "사과";},
()->{Thread.sleep(300); return "바나나";},
()->{Thread.sleep(700); return "포도";}
);
List<Future<String>> futures = executor.invokeAll(tasks);
for (Future<String> f:futures){
try{
System.out.println("결과: " + f.get());
}catch(ExecutionException e){
System.out.println("작업 중 예외 발생: " + e.getCause());
}
}
executor.shutdown();
}
}
여러 Callable<T> 작업을 제출하고, 가장 먼저 완료된(성공적으로 반환된) 하나의 결과만 리턴한다. 나머지 작업은 취소된다.
(invokeAny(tasks, timeout, unit)).import java.util.*;
import java.util.concurrent.*;
public class InvokeAnyExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
List<Callable<String>> tasks = List.of(
() -> { Thread.sleep(500); return "🍎 사과"; },
() -> { Thread.sleep(300); return "🍌 바나나"; },
() -> { Thread.sleep(700); return "🍇 포도"; }
);
try {
// 가장 먼저 끝난 작업의 결과만 리턴
String result = executor.invokeAny(tasks);
System.out.println("가장 빠른 결과: " + result);
} catch (InterruptedException | ExecutionException e) {
System.out.println("예외 발생: " + e.getMessage());
} finally {
executor.shutdown();
}
}
}
package lesson08;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
class Ex14_Order{
private final int orderId;
public Ex14_Order(int orderId){
this.orderId = orderId;
}
public int getOrderId(){
return orderId;
}
}
class Ex14_OrderProcessor implements Callable<String>{
private final Ex14_Order order;
private final String workerName;
private static final Random random = new Random();
private static final Map<String, int[]> workerSpeedMap = Map.of(
"김사과", new int[]{1000, 2000},
"반하나", new int[]{2000, 3000},
"오렌지", new int[]{3000, 4000}
);
private static final AtomicInteger totalProcessed = new AtomicInteger(0);
private static final Map<String, Integer> workerStats = new ConcurrentHashMap<>();
public Ex14_OrderProcessor(Ex14_Order order, String workerName){
this.order = order;
this.workerName = workerName;
}
@Override
public String call() throws Exception {
int[] speedRange = workerSpeedMap.get(workerName);
int prepTime = random.nextInt(speedRange[1]-speedRange[0]+1)+speedRange[0];
Thread.sleep(prepTime);
// 주문 1건이 처리되었으니, 전체 주문 수를 1 증가
totalProcessed.incrementAndGet();
// workerName: 아르바이트생 이름
// 1: 처리한 주문 수
// 아르바이트생 이름이 Map에 없으면 (workerName, 1)로 추가
// 이미 있다면 ("김사과", 기존값 + 1) 로 업데이트
workerStats.merge(workerName, 1, Integer::sum);
return workerName + " - 주문 " + order.getOrderId() + "번 완료 (소요시간 : " + prepTime + "ms)";
}
public static int getTotalProcessed(){
return totalProcessed.get();
}
public static void printStats(){
System.out.println("점원별 처리 주문 수: ");
workerStats.forEach((name, count)-> System.out.println(name + ": " + count + "건"));
}
}
public class Ex14_Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
int totalOrders = 10;
List<String> workers = List.of("김사과", "반하나", "오렌지");
ExecutorService executor = Executors.newFixedThreadPool(workers.size());
List<Future<String>> futures = new ArrayList<>();
for(int i =1; i<=totalOrders; i++){
String worker = workers.get(i%workers.size());
Ex14_Order order = new Ex14_Order(i);
futures.add(executor.submit(new Ex14_OrderProcessor(order, worker)));
}
for(Future<String> future : futures){
System.out.println(future.get());
}
executor.shutdown();
Ex14_OrderProcessor.printStats();
System.out.println("총 주문 처리 수: " + Ex14_OrderProcessor.getTotalProcessed() + "건");
}
}