[Java] 생산자-소비자 문제와 Object 클래스

MEUN·2024년 11월 13일
0

생산자-소비자 문제란?

개요

  • 여러 개의 프로세스를 어떻게 동기화할 것인지에 대한 문제로, 여러 스레드가 동시에 데이터를 생산 및 소비하는 과정에서 발생하는 문제를 말한다.
  • 버퍼의 크기가 한정되어 있어 발생하는 문제이기 때문에 한정 버퍼 문제라고도 불린다.

문제 상황

한정된 버퍼가 존재하는 상황에서 아래와 같은 상황이 발생할 수 있다.

  1. 버퍼가 가득 찬 경우, 생산자는 버퍼에 여유가 생길 때까지 대기
  2. 버퍼가 비어있는 경우, 소비자는 버퍼에 데이터가 존재할 때까지 대기



예상 처리 방안

  1. 버퍼가 가득 찬 경우 생산자는 데이터를 생성하지 않거나 버퍼가 빈 경우 소비자는 데이터를 획득하지 않음 (❌)
  2. 버퍼가 가득 찬 경우 생산자는 버퍼에 여유가 생길 때까지 대기, 버퍼가 빈 경우 소비자는 버퍼에 데이터가 생성될 때까지 대기 (⭕)

2번 방식으로 처리하기 위해서는 생산자 및 소비자 Thread가 락을 획득하고 무한대기 하지 않도록 아래와 같은 작업이 필요하다.

  • 현재 작업이 불가한 경우 락 반납 후 대기
  • 작업 후 락을 반납한 뒤 대기중인 Thread를 깨움

이때, Object 클래스의 wait(), notify(), notifyAll() 사용이 가능하다.



Object 클래스의 Thread 동기화 지원

유관 메서드

wait()

public final void wait() throws InterruptedException
  • 인터럽트 되거나 깨어날 때까지(Notify 알림을 받을 때까지) 대기
  • wait(0L, 0) 이 호출된 것처럼 동작
  • 호출 시 Thread의 상태는 WAITING으로 변경

notify()

public final void notify()
  • wait() 메서드를 통해 해당 객체의 모니터에서 대기중인 Thread 중 하나를 깨움
    • 선택은 임의적이며 구현의 재량에 따라 달라짐
  • 현재 락을 획득한 Thread에서 호출 가능, 아닌 Thread가 호출 시 IllegalMonitorStateException 발생
  • 해당 메서드 호출을 통해 깨어난 스레드는 락 획득 전까지 진행 불가

notifyAll()

public final void notifyAll()
  • wait() 메서드를 통해 해당 객체의 모니터에서 대기중인 전체 Thread를 깨움
  • 기타 다른 내용은 notify()와 유사



notify() 의 한계

비효율

  • 특정 Thread를 깨울 수 없어 동일 타입의 Thread를 깨울 때 비효율 발생 가능
    - ex) 버퍼가 비어있을 때 소비자 Thread가 다른 소비자 Thread를 깨우는 경우

기아 상태

  • 특정 Thread를 깨울 수 없어 특정 스레드를 제외하고 깨울 수 있는 상황 발생 가능
    - ex) 생산자 및 소비자 Thread가 WAITING 상태일 때 소비자 Thread들만 깨어날 수 있음



Javascript의 동시성

위 내용을 작성하던 중 자바스크립트의 동작에 관해 궁금해졌다.

Javascript는 Single Thread로 동작하는 언어라 한 번에 하나의 작업만을 수행할 수 있다.

동시성을 보장하는 비동기, 논블로킹 작업들은 Javascript 엔진을 구동하는 런타임 환경에서 이루어진다.

이때, 런타임 환경이란 브라우저 혹은 Node.js 등을 의미한다.

하지만 정확히는 비동기 프로그래밍 패턴과 이벤트 루프를 통해 동시성을 지원하는 것처럼 보이게 할 수 있게 하는 것인데, 주요 방식은 아래와 같다.

이벤트 루프

  • 이벤트 루프를 통해 호출되는 콜백 함수 관리
  • 비동기 작업 발생 시 큐에 작업들이 추가되고, 이벤트 루프는 순차적으로 콜백 함수 실행

비동기 처리

async function fetchData() {
    try {
        let response = await fetch('https://api.example.com/data');
        let data = await response.json();
        console.log(data);
    } catch (error) {
        console.error(error);
    }
}
fetchData();
  • Promise : 비동기 작업의 완료 또는 실패를 나타내는 객체
  • async : function 앞에 사용하여 비동기 함수 정의
  • await : [rv] = await expression; 형태로 사용되며, 프로미스를 기다리기 위해 사용
    • async function 내부에서만 사용 가능


자세한 내용은 추후 다른 글을 통해 더 깊이 공부해보고 싶다.

참고 자료

0개의 댓글