티스토리 뷰

세마포어(Semaphore), 배리어(Barrier), 래치(Latch)란?

멀티스레드 또는 분산 시스템에서 동기화(Synchronization) 및 동시성 제어(Concurrency Control)를 위해 사용하는 주요 개념입니다.

  • 세마포어(Semaphore): 공유 자원의 접근을 제한하는 메커니즘
  • 배리어(Barrier): 특정 지점까지 모든 스레드가 도착해야 다음 단계로 진행할 수 있도록 하는 동기화 도구
  • 래치(Latch): 특정 조건이 충족될 때까지 스레드를 차단하는 동기화 메커니즘

 

이제 각 개념을 자세히 살펴보겠습니다.

 

1. 세마포어(Semaphore)

세마포어는 공유 자원(예: 파일, 데이터베이스, 메모리 등)에 대한 접근을 제한하는 동기화 기법입니다.
일반적으로 카운터(counter) 값을 기반으로 동작하며, 이를 통해 몇 개의 스레드가 동시에 자원에 접근할 수 있는지 결정합니다.

 

세마포어의 종류

  1. 이진 세마포어(Binary Semaphore, Mutex와 유사)
    • 값이 0 또는 1만 가질 수 있는 세마포어
    • 한 번에 하나의 스레드만 자원에 접근 가능
    • 뮤텍스(Mutex)와 유사하지만, 소유권 개념이 없음
  2. 카운팅 세마포어(Counting Semaphore)
    • 임의의 정수 값을 가질 수 있으며, 특정 개수만큼의 스레드가 동시에 접근 가능
    • ex) DB Connection Pool의 최대 연결 개수 제한

 

세마포어의 동작

세마포어는 두 가지 연산을 수행합니다.

  1. wait() (P 연산, acquire)
    • 세마포어 값을 감소시키고, 값이 0보다 작아지면 대기 상태로 진입
  2. signal() (V 연산, release)
    • 세마포어 값을 증가시키고, 대기 중인 스레드가 있으면 깨움

 

세마포어 예제 (Java)

import java.util.concurrent.Semaphore;

class SharedResource {
    private final Semaphore semaphore = new Semaphore(2); // 최대 2개 스레드 접근 가능

    public void accessResource() {
        try {
            semaphore.acquire(); // wait() 연산
            System.out.println(Thread.currentThread().getName() + "가 자원에 접근 중...");
            Thread.sleep(2000); // 작업 수행
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release(); // signal() 연산
            System.out.println(Thread.currentThread().getName() + "가 자원 사용 종료.");
        }
    }
}

public class SemaphoreExample {
    public static void main(String[] args) {
        SharedResource resource = new SharedResource();
        for (int i = 0; i < 5; i++) {
            new Thread(resource::accessResource).start();
        }
    }
}
  • Semaphore(2) → 최대 2개의 스레드가 동시에 접근 가능
  • 추가적인 스레드는 대기하다가 release()가 호출되면 실행됨

 

 

 

2. 배리어(Barrier)

배리어는 모든 스레드가 특정 지점에 도달해야만 다음 단계로 진행할 수 있도록 하는 동기화 메커니즘입니다.

 

배리어의 특징

  • 모든 스레드가 특정 "점(barrier)"에 도달해야 다음 단계로 진행 가능
  • 멀티스레드 병렬 연산에서 작업이 동기적으로 실행되도록 보장
  • 일반적으로 병렬 컴퓨팅 및 분산 시스템에서 데이터 동기화 및 병렬 계산을 조율할 때 사용

 

배리어의 동작

  • await() 호출 시, 모든 스레드가 도착할 때까지 대기
  • 모든 스레드가 await()을 호출하면 동시에 다음 단계로 진행됨

 

배리어 예제 (Java - CyclicBarrier)

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

class Worker extends Thread {
    private CyclicBarrier barrier;

    public Worker(CyclicBarrier barrier) {
        this.barrier = barrier;
    }

    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName() + " 작업 수행 중...");
            Thread.sleep((long) (Math.random() * 3000));
            System.out.println(Thread.currentThread().getName() + " 배리어 도착");
            barrier.await(); // 모든 스레드가 도착할 때까지 대기
            System.out.println(Thread.currentThread().getName() + " 다음 작업 시작");
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
}

public class BarrierExample {
    public static void main(String[] args) {
        final int threadCount = 3;
        CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> System.out.println("모든 스레드가 도착! 다음 단계 실행"));

        for (int i = 0; i < threadCount; i++) {
            new Worker(barrier).start();
        }
    }
}
  • CyclicBarrier(3) → 3개의 스레드가 도착해야 다음 단계로 진행됨
  • 모든 스레드가 await()을 호출하면 다음 단계 실행

 

 

 

3. 래치(Latch)

래치는 특정 조건이 충족될 때까지 스레드가 대기하는 동기화 메커니즘입니다.

 

래치의 특징

  • 특정 이벤트가 발생할 때까지 스레드를 블로킹
  • countDown()을 호출하여 래치를 감소시키고, 값이 0이 되면 대기 중인 스레드가 모두 실행됨
  • 초기 설정한 값만큼 이벤트가 발생해야 스레드가 해제됨
  • 일반적으로 애플리케이션 초기화 및 비동기 작업 완료를 대기할 때 사용

 

래치 예제 (Java - CountDownLatch)

import java.util.concurrent.CountDownLatch;

class Task extends Thread {
    private CountDownLatch latch;

    public Task(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName() + " 작업 수행 중...");
            Thread.sleep((long) (Math.random() * 3000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            latch.countDown(); // 래치 값 감소
            System.out.println(Thread.currentThread().getName() + " 작업 완료. 남은 래치: " + latch.getCount());
        }
    }
}

public class LatchExample {
    public static void main(String[] args) throws InterruptedException {
        final int threadCount = 3;
        CountDownLatch latch = new CountDownLatch(threadCount);

        for (int i = 0; i < threadCount; i++) {
            new Task(latch).start();
        }

        latch.await(); // 모든 스레드가 완료될 때까지 대기
        System.out.println("모든 작업이 완료됨! 메인 스레드 실행");
    }
}
  • CountDownLatch(3) → 3개의 스레드가 완료될 때까지 await()에서 대기
  • 각 스레드가 작업을 마칠 때마다 countDown() 호출 → 값이 0이 되면 await()이 해제됨

 

정리

개념 설명 사용 예시

세마포어(Semaphore) 공유 자원의 접근을 제한하는 동기화 메커니즘 데이터베이스 연결 풀, 쓰레드 동시 실행 제한
배리어(Barrier) 모든 스레드가 특정 지점에 도달해야 다음 단계 진행 가능 병렬 계산 동기화, 분산 작업 조율
래치(Latch) 특정 조건이 충족될 때까지 스레드 차단 애플리케이션 초기화, 비동기 작업 완료 대기

 

각 개념은 동시성 제어 및 시스템 안정성을 확보하는 데 중요한 역할을 합니다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/03   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
글 보관함