JAVA

221031 생산자와 소비자 문제

주영재 2022. 10. 31. 12:24

11.생산자와 소비자 문제
=>생산자와 소비자는 도시에 수행할 수 있는데 소비자는 생산자가 자원을 생성해준 경우에만 동작을 해야 함
소비자가 자원이 생성되지 않았는데 작업을 수행하면 예외가 발생

1)생산자와 소비자 문제 발생
=>공유자원의 역할을 수행할 클래스-Product

=>생산자 스레드 클래스 : Producer
=>소비자 스레드 클래스 : Customer
=>main메서드를 소유한 클래스를 만들어서 스레드 2개를 실행

=>실행을 하다보면 예외가 발생
소비자 스레드인 Customer스레드가 리스트에 아무런 데이터가 없는데 꺼낼려고 해서 발생하는 문제
소비자 스레드의 메서드는 데이터가 없을 때는 대기하고  생산자 스레드는 데이터를 만들어낸 경우 데이터가 만들어졌다고 알려주어야 함.
기다리기 위해서 호출하는 메서드는 wait이고 알려주는 메서드는 notify와 notifyAll이라는 메서드임
이 메서드들은 Object클래스의 메서드이고 synchronized메서드 안에서만 동작합니다 

=>Product 클래스를 수정하고 실행
다시 실행하면 예외가 발생하지 않음

 

Product

package java_1031.producer.consumer;

import java.util.ArrayList;
import java.util.List;

//공유 자원의 역할을 수행할 클래스-진열대 역할
public class Product {
	//문자를 저장할 수 있는 List-공유 자원
	List<Character> list;
	
	//생성자
	public Product() {
		list=new ArrayList<>();
	}
	
	//생산자 메서드
	public synchronized void put(Character ch) {
		list.add(ch);
		System.out.println("창고에 제품 "+ch+"가 입고 되었습니다.");
		try {
			Thread.sleep(1000);
			System.out.println("재고 수량:"+list.size());
		} catch (Exception e) {
			//자원을 생산했다고 알려줌
			notify();
		}
		
	}
	
	//소비자 메서드
	public synchronized void get() {
		try{
		if(list.size()==0) {
			wait();
		}
		}catch(Exception e) {}
		//첫번째 데이터를 꺼내서 ch에 대입
		Character ch=list.remove(0);
		System.out.println("창고에 제품 "+ch+"를 출고 하였습니다.");
		try {
			Thread.sleep(1000);
			System.out.println("재고 수량:"+list.size());
		} catch (Exception e) {
		}
	}
	
	
}

 

Product 상속받는 Produce, Customer

package java_1031.producer.consumer;

public class Producer extends Thread{
	//공유자원 속성
	private Product myList;
	
	//생성자
	public Producer(Product myList) {
	 this.myList=myList;	
	} 
		
	@Override
	public void run() {
		//삽입 작업을 26번 수행
		for(char ch = 'A';ch<='Z';ch++) {
			myList.put(ch);
		}
	}
}

 

package java_1031.producer.consumer;

public class Customer extends Thread{
	public Product myList;
	
	public Customer(Product myList) {
		this.myList=myList;
	}
	
	@Override
	public void run() {
		for(char ch ='A';ch<='Z';ch++) {
			myList.get();
		}
	}
}

 

main

package java_1031.producer.consumer;

public class Main {
	public static void main(String[] args) {
		//자원 생성
		Product product = new Product();
		//하나의 자원을 이용해서 2개의 스레드를 생성해서 실행
		new Producer(product).start();
		new Customer(product).start();
	}

}
창고에 제품 A가 입고 되었습니다.
재고 수량:1
창고에 제품 B가 입고 되었습니다.
재고 수량:2
창고에 제품 C가 입고 되었습니다.
재고 수량:3
창고에 제품 D가 입고 되었습니다.
재고 수량:4
창고에 제품 E가 입고 되었습니다.
재고 수량:5
창고에 제품 F가 입고 되었습니다.
재고 수량:6
창고에 제품 G가 입고 되었습니다.
재고 수량:7
창고에 제품 H가 입고 되었습니다.
재고 수량:8
창고에 제품 A를 출고 하였습니다.
재고 수량:7
창고에 제품 B를 출고 하였습니다.
재고 수량:6
창고에 제품 C를 출고 하였습니다.
재고 수량:5
창고에 제품 D를 출고 하였습니다.
재고 수량:4
창고에 제품 E를 출고 하였습니다.
재고 수량:3
창고에 제품 F를 출고 하였습니다.
재고 수량:2
창고에 제품 G를 출고 하였습니다.
재고 수량:1
창고에 제품 I가 입고 되었습니다.
재고 수량:2
창고에 제품 J가 입고 되었습니다.
재고 수량:3
창고에 제품 K가 입고 되었습니다.
재고 수량:4
창고에 제품 L가 입고 되었습니다.
재고 수량:5
창고에 제품 M가 입고 되었습니다.
재고 수량:6
창고에 제품 N가 입고 되었습니다.
재고 수량:7
창고에 제품 H를 출고 하였습니다.
재고 수량:6
창고에 제품 I를 출고 하였습니다.
재고 수량:5
창고에 제품 O가 입고 되었습니다.
재고 수량:6
창고에 제품 P가 입고 되었습니다.
재고 수량:7
창고에 제품 Q가 입고 되었습니다.
재고 수량:8
창고에 제품 J를 출고 하였습니다.
재고 수량:7
창고에 제품 K를 출고 하였습니다.
재고 수량:6
창고에 제품 L를 출고 하였습니다.
재고 수량:5
창고에 제품 M를 출고 하였습니다.
재고 수량:4
창고에 제품 N를 출고 하였습니다.
재고 수량:3
창고에 제품 O를 출고 하였습니다.
재고 수량:2
창고에 제품 R가 입고 되었습니다.
재고 수량:3
창고에 제품 S가 입고 되었습니다.
재고 수량:4
창고에 제품 T가 입고 되었습니다.
재고 수량:5
창고에 제품 U가 입고 되었습니다.
재고 수량:6
창고에 제품 V가 입고 되었습니다.
재고 수량:7
창고에 제품 W가 입고 되었습니다.
재고 수량:8
창고에 제품 X가 입고 되었습니다.
재고 수량:9
창고에 제품 Y가 입고 되었습니다.
재고 수량:10
창고에 제품 Z가 입고 되었습니다.
재고 수량:11
창고에 제품 P를 출고 하였습니다.
재고 수량:10
창고에 제품 Q를 출고 하였습니다.
재고 수량:9
창고에 제품 R를 출고 하였습니다.
재고 수량:8
창고에 제품 S를 출고 하였습니다.
재고 수량:7
창고에 제품 T를 출고 하였습니다.
재고 수량:6
창고에 제품 U를 출고 하였습니다.
재고 수량:5
창고에 제품 V를 출고 하였습니다.
재고 수량:4
창고에 제품 W를 출고 하였습니다.
재고 수량:3
창고에 제품 X를 출고 하였습니다.
재고 수량:2
창고에 제품 Y를 출고 하였습니다.
재고 수량:1
창고에 제품 Z를 출고 하였습니다.
재고 수량:0

스레드는 어떤게 먼저 실행될지 알 수 없으니 입출고의 순서가 실행할 때마다 다르다

단, myList의 사이즈가 0일 때 출고를 하는 것은 불가능하므로 반드시 처음 입고를 한 뒤 출고가 실행된다.