6.8 KiB
자바 동시성(Concurrency) API 정리 및 쉬운 설명
자바의 동시성(Concurrency) API는 멀티스레딩을 효율적으로 관리하고 안전하게 실행할 수 있도록 지원하는 기능을 제공한다.
즉, 여러 작업을 동시에 실행하여 프로그램 성능을 향상시킬 수 있다.
1. 주요 클래스 및 메서드 정리
(1) Thread 클래스 (기본 스레드 실행)
| 메서드 | 설명 |
|---|---|
start() |
새로운 스레드를 실행 |
run() |
실행할 코드 정의 (직접 호출하면 동작하지 않음) |
sleep(ms) |
지정된 시간(ms) 동안 스레드 일시 정지 |
join() |
현재 스레드가 종료될 때까지 다른 스레드 대기 |
interrupt() |
실행 중인 스레드를 인터럽트 (깨우기) |
isAlive() |
스레드가 실행 중인지 확인 |
사용 예시:
class MyThread extends Thread {
public void run() {
System.out.println("스레드 실행 중!");
}
}
MyThread t = new MyThread();
t.start(); // 스레드 실행
(2) Runnable 인터페이스 (스레드 실행)
| 메서드 | 설명 |
|---|---|
run() |
실행할 코드 정의 |
사용 예시:
class MyRunnable implements Runnable {
public void run() {
System.out.println("Runnable 스레드 실행!");
}
}
Thread t = new Thread(new MyRunnable());
t.start();
→ Thread 클래스를 직접 상속하지 않고 Runnable을 구현하여 사용.
(3) ExecutorService (스레드 풀)
| 메서드 | 설명 |
|---|---|
submit(Runnable) |
스레드를 실행 (결과 없음) |
submit(Callable<T>) |
스레드를 실행하고 결과 반환 |
shutdown() |
스레드 풀 종료 (기존 작업 수행 후 종료) |
shutdownNow() |
즉시 모든 작업 중단 |
사용 예시:
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.submit(() -> System.out.println("스레드 풀에서 실행"));
executor.shutdown();
→ Executors.newFixedThreadPool(2)를 사용해 2개의 스레드를 관리.
(4) Callable & Future (결과 반환이 필요한 작업)
| 메서드 | 설명 |
|---|---|
call() |
실행할 코드 정의, 결과 반환 |
get() |
Future에서 결과 가져오기 (블로킹) |
isDone() |
작업 완료 여부 확인 |
사용 예시:
Callable<Integer> task = () -> {
Thread.sleep(1000);
return 10;
};
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(task);
System.out.println(future.get()); // 결과 가져오기
executor.shutdown();
→ Callable을 사용하면 스레드 실행 후 결과 값을 반환할 수 있음.
(5) ReentrantLock (락을 이용한 동기화)
| 메서드 | 설명 |
|---|---|
lock() |
락 획득 |
unlock() |
락 해제 |
tryLock() |
락을 시도하고 성공하면 true 반환 |
사용 예시:
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
System.out.println("임계영역 실행");
} finally {
lock.unlock();
}
→ synchronized 키워드 대신 ReentrantLock을 사용하여 더 정교한 동기화 가능.
(6) Semaphore (동시 실행 제한)
| 메서드 | 설명 |
|---|---|
acquire() |
리소스 사용 요청 (없으면 대기) |
release() |
사용한 리소스 반환 |
사용 예시:
Semaphore semaphore = new Semaphore(2);
semaphore.acquire(); // 사용 가능하면 진행, 아니면 대기
System.out.println("리소스 사용 중");
semaphore.release();
→ 한 번에 2개의 스레드만 특정 코드 실행 가능.
(7) CountDownLatch (스레드가 모두 끝날 때까지 대기)
| 메서드 | 설명 |
|---|---|
await() |
모든 스레드가 완료될 때까지 대기 |
countDown() |
하나의 작업 완료 처리 |
사용 예시:
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println("작업 완료");
latch.countDown();
}).start();
}
latch.await();
System.out.println("모든 작업 완료");
→ 모든 작업(countDown() 3회)이 끝나야 await()이 풀림.
(8) CyclicBarrier (스레드가 모두 도달할 때까지 대기)
| 메서드 | 설명 |
|---|---|
await() |
지정된 개수의 스레드가 모일 때까지 대기 |
사용 예시:
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("모든 스레드 도착!");
});
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println("스레드 실행 중");
barrier.await();
}).start();
}
→ CyclicBarrier는 정해진 개수의 스레드가 모두 도착할 때까지 기다림.
2. 자바 동시성 쉽게 설명하기
자바에서 동시성을 다룰 때 중요한 개념은 멀티스레딩과 스레드 동기화다.
즉, 여러 개의 스레드가 같은 자원을 동시에 접근하면 충돌이 발생할 수 있으므로, 이를 적절히 제어해야 한다.
1️⃣ 기본적인 멀티스레드
Thread또는Runnable을 사용해 실행start()로 실행,join()으로 대기
2️⃣ 스레드 풀 사용 (ExecutorService)
- 직접 스레드를 생성하면 관리가 어렵기 때문에, 스레드 풀을 사용하여 성능 최적화
submit()으로 작업을 실행하고,shutdown()으로 종료
3️⃣ 결과를 받아야 한다면? (Callable & Future)
Callable을 사용하면 작업이 끝난 후 결과 값을 반환 가능future.get()을 호출하면 결과를 가져올 수 있음
4️⃣ 공유 자원 충돌 방지 (ReentrantLock, synchronized)
synchronized키워드는 단순하지만 유연성이 낮음ReentrantLock은 더 정교한 동기화 제공
5️⃣ 특정 조건을 만족할 때 실행 (CountDownLatch, CyclicBarrier)
CountDownLatch→ 여러 개의 스레드가 끝날 때까지 대기CyclicBarrier→ 여러 스레드가 동시에 도달해야 실행
3. 정리
✅ 멀티스레딩을 쉽게 하려면? → ExecutorService
✅ 스레드 실행 결과를 받아야 한다면? → Callable, Future
✅ 여러 스레드가 동시에 공유 자원을 사용하면? → ReentrantLock, synchronized
✅ 여러 작업이 끝난 후 실행되게 하려면? → CountDownLatch, CyclicBarrier
즉, 자바 동시성 API를 적절히 활용하면 멀티스레딩을 효율적으로 관리할 수 있다!