Condition

sungs·2025년 7월 13일

자바

목록 보기
42/95

Condition

스레드 대기 집합을 만들어 내는 객체라 할 수 있다. 객체처럼 private final Condition 컨디션 이름 = ReentrantLock 락 이름.newCondition()으로 만들어 내며, 이렇게 해서 여러 개의 스레드 대기 집합 만들어 낼 수 있다.
이렇게 만들어진 Condition에다가 여러 스레드를 원하는 대로 구별해서 넣어두면 wait(), notify()의 한계점이었던 '원하는 스레드를 깨울 수 없다'라는 점을 극복할 수 있다.
생산자-소비자 문제로 따지면 생산자 스레드만 따로 모아운 Condition을 깨우거나 소비자 스레드만 따로 모아운 Condition을 깨우거나 그럴 수 있게 되는 것이다. 이렇게 하면 원하는 스레드만 깨울 수 있어 좀 더 효율적인 작업이 가능해진다.

await()

대기시키고자하는 컨디션 이름.await()로 사용되며 용도는 wait()랑 비슷하다. 인스턴스에 있는 스레드 대기 집합에 스레드를 보내는 게 아니라 컨디션의 스레드 대기 공간으로 스레드를 보내는 것뿐.

signal()

꺠우고자 하는 컨디션 이름.signal()롤 사용되면 용도는 notify()와 똑같다. 이것도 컨디션의 스레드 대기 공간을 호출한다는 점만 다르다.

예제

import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

// 공유 버퍼 클래스
class BufferWithCondition {
    private Queue<Integer> queue;
    private int capacity; // 버퍼의 최대 용량

    // Lock 객체 생성 (ReentrantLock 사용)
    private final Lock lock = new ReentrantLock();

    // Condition 객체 생성: 버퍼가 가득 찼을 때 생산자 스레드를 대기시킬 조건
    private final Condition notFull = lock.newCondition();
    // Condition 객체 생성: 버퍼가 비었을 때 소비자 스레드를 대기시킬 조건
    private final Condition notEmpty = lock.newCondition();

    public BufferWithCondition(int capacity) {
        this.queue = new LinkedList<>();
        this.capacity = capacity;
    }

    // 생산자가 데이터를 버퍼에 추가하는 메서드
    public void put(int value) throws InterruptedException {
        // 락 획득
        lock.lock();
        try {
            // 버퍼가 가득 찼으면 생산자는 notFull 조건에서 대기
            while (queue.size() == capacity) {
                System.out.println("버퍼 가득 참: 생산자 대기 중...");
                notFull.await(); // notFull 조건에서 대기하며 락을 반납
            }

            // 버퍼에 데이터 추가
            queue.offer(value);
            System.out.println("생산: " + value + " (현재 크기: " + queue.size() + ")");

            // 데이터를 추가했으니, notEmpty 조건에서 대기 중인 소비자에게 알림
            notEmpty.signalAll(); // notEmpty 조건에서 대기 중인 모든 스레드를 깨움
        } finally {
            // 락 반납 (중요! 예외 발생 시에도 락이 풀리도록 finally 블록 사용)
            lock.unlock();
        }
    }

    // 소비자가 데이터를 버퍼에서 가져가는 메서드
    public int get() throws InterruptedException {
        int value;
        // 락 획득
        lock.lock();
        try {
            // 버퍼가 비어있으면 소비자는 notEmpty 조건에서 대기
            while (queue.isEmpty()) {
                System.out.println("버퍼 비어있음: 소비자 대기 중...");
                notEmpty.await(); // notEmpty 조건에서 대기하며 락을 반납
            }

            // 버퍼에서 데이터 가져오기
            value = queue.poll();
            System.out.println("소비: " + value + " (현재 크기: " + queue.size() + ")");

            // 데이터를 가져갔으니, notFull 조건에서 대기 중인 생산자에게 알림
            notFull.signalAll(); // notFull 조건에서 대기 중인 모든 스레드를 깨움
        } finally {
            // 락 반납 (중요!)
            lock.unlock();
        }
        return value;
    }
}

// 생산자 스레드 클래스 (BufferWithCondition 사용)
class ProducerWithCondition extends Thread {
    private BufferWithCondition buffer;
    private Random random = new Random();

    public ProducerWithCondition(BufferWithCondition buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        try {
            for (int i = 0; i < 10; i++) { // 10개의 데이터를 생산
                int value = random.nextInt(100);
                buffer.put(value);
                Thread.sleep(random.nextInt(500));
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.out.println("생산자 스레드 인터럽트됨.");
        }
    }
}

// 소비자 스레드 클래스 (BufferWithCondition 사용)
class ConsumerWithCondition extends Thread {
    private BufferWithCondition buffer;
    private Random random = new Random();

    public ConsumerWithCondition(BufferWithCondition buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        try {
            for (int i = 0; i < 10; i++) { // 10개의 데이터를 소비
                buffer.get();
                Thread.sleep(random.nextInt(500));
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.out.println("소비자 스레드 인터럽트됨.");
        }
    }
}

public class ConditionExample {
    public static void main(String[] args) {
        BufferWithCondition buffer = new BufferWithCondition(5); // 용량이 5인 버퍼 생성

        ProducerWithCondition producer = new ProducerWithCondition(buffer);
        ConsumerWithCondition consumer = new ConsumerWithCondition(buffer);

        producer.start();
        consumer.start();

        try {
            producer.join();
            consumer.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.out.println("메인 스레드 인터럽트됨.");
        }

        System.out.println("\n--- Condition을 이용한 생산자-소비자 작업 완료 ---");
    }
}
profile
앱 개발 공부 중

0개의 댓글