Files
java-examples/docs/Concurrency.md

6.8 KiB
Raw Permalink Blame History

자바 동시성(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를 적절히 활용하면 멀티스레딩을 효율적으로 관리할 수 있다!