Java 다중 스레드 동시 협업 생산자-소비자 디자인 패턴

伊谢尔伦
풀어 주다: 2016-11-21 13:31:37
원래의
1771명이 탐색했습니다.

두 개의 스레드, 하나의 생산자 및 하나의 소비자

수요 시나리오

두 개의 스레드, 하나는 생산을 담당하고 다른 하나는 소비를 담당합니다.

관련 문제

동기화 문제: 여러 스레드가 동시에 액세스할 때 동일한 리소스의 무결성을 보장하는 방법. 일반적으로 사용되는 동기화 방법은 표시 또는 잠금 메커니즘을 사용하는 것입니다

wait() / nofity() 메소드는 기본 클래스 Object의 두 가지 메소드이므로 모든 Java 클래스가 이 두 가지 메소드를 갖습니다. , 우리는 모든 객체에 대해 동기화 메커니즘을 구현할 수 있습니다.

Wait() 메서드: 버퍼가 가득 차거나 비면 생산자/소비자 스레드는 자체 실행을 중지하고 잠금을 포기하고 대기 상태에 들어가 다른 스레드가 실행되도록 허용합니다.

Notify() 메서드: 생산자/소비자가 제품을 버퍼에 넣거나 꺼낼 때 대기 중인 다른 스레드에 실행 가능한 알림을 보내는 동시에 잠금을 포기하고 자신을 대기 상태.

코드 구현(총 3개 클래스와 메인 메소드가 포함된 테스트 클래스)

  Resource.java
  /**
  * Created by yuandl on 2016-10-11./**
  * 资源
  */
  public class Resource {
  /*资源序号*/
  private int number = 0;
  /*资源标记*/
  private boolean flag = false;
  /**
  * 生产资源
  */
  public synchronized void create() {
  if (flag) {//先判断标记是否已经生产了,如果已经生产,等待消费;
  try {
  wait();//让生产线程等待
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  }
  number++;//生产一个
  System.out.println(Thread.currentThread().getName() + "生产者------------" + number);
  flag = true;//将资源标记为已经生产
  notify();//唤醒在等待操作资源的线程(队列)
  }
  /**
  * 消费资源
  */
  public synchronized void destroy() {
  if (!flag) {
  try {
  wait();
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  }
  System.out.println(Thread.currentThread().getName() + "消费者****" + number);
  flag = false;
  notify();
  }
 }
로그인 후 복사

Producer.java

  /**
  * Created by yuandl on 2016-10-11.
  *
  /**
  * 生产者
  */
  public class Producer implements Runnable {
  private Resource resource;
  public Producer(Resource resource) {
  this.resource = resource;
  }
  @Override
  public void run() {
  while (true) {
  try {
  Thread.sleep(10);
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  resource.create();
  }
  }
  }
로그인 후 복사

Consumer.java

 /**
  * 消费者
  */
  public class Consumer implements Runnable {
  private Resource resource;
  public Consumer(Resource resource) {
  this.resource = resource;
  }
  @Override
  public void run() {
  while (true) {
  try {
  Thread.sleep(10);
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  resource.destroy();
  }
  }
  }
로그인 후 복사

ProducerConsumerTest.java

  /**
  * Created by yuandl on 2016-10-11.
  */
  public class ProducerConsumerTest {
  public static void main(String args[]) {
  Resource resource = new Resource();
  new Thread(new Producer(resource)).start();//生产者线程
  new Thread(new Consumer(resource)).start();//消费者线程
  }
  }
로그인 후 복사

인쇄 결과

Thread-0生产者------------1
 Thread-1消费者****1
  Thread-0生产者------------2
  Thread-1消费者****2
  Thread-0生产者------------3
  Thread-1消费者****3
  Thread-0生产者------------4
  Thread-1消费者****4
  Thread-0生产者------------5
  Thread-1消费者****5
  Thread-0生产者------------6
  Thread-1消费者****6
  Thread-0生产者------------7
  Thread-1消费者****7
  Thread-0生产者------------8
  Thread-1消费者****8
  Thread-0生产者------------9
  Thread-1消费者****9
  Thread-0生产者------------10
  Thread-1消费者****10
로그인 후 복사

위의 인쇄 결과는 문제가 없음을 보여줍니다


다중 스레드, 다중 생산자 및 다중 소비자 문제

수요 시나리오

4개의 스레드, 2개가 생산을 담당하고, 2개가 소비를 담당하고, 생산자가 1개를 생산하고, 소비자가 1개를 소비합니다.

관련 문제

informAll() 메소드: 생산자/소비자가 버퍼에서 제품을 넣거나 꺼낼 때 다른 모든 대기 스레드에 실행 가능한 알림을 보내는 동시에 잠금을 포기하고 대기 상태에 들어갑니다.

코드 다시 테스트

 ProducerConsumerTest.java
  /**
  * Created by yuandl on 2016-10-11.
  */
  public class ProducerConsumerTest {
  public static void main(String args[]) {
  Resource resource = new Resource();
  new Thread(new Consumer(resource)).start();//生产者线程
  new Thread(new Consumer(resource)).start();//生产者线程
  new Thread(new Producer(resource)).start();//消费者线程
  new Thread(new Producer(resource)).start();//消费者线程
  }
  }
로그인 후 복사

실행 결과

  Thread-0生产者------------100
  Thread-3消费者****100
  Thread-0生产者------------101
  Thread-3消费者****101
  Thread-2消费者****101
  Thread-1生产者------------102
  Thread-3消费者****102
  Thread-0生产者------------103
  Thread-2消费者****103
  Thread-1生产者------------104
  Thread-3消费者****104
  Thread-1生产者------------105
  Thread-0生产者------------106
  Thread-2消费者****106
  Thread-1生产者------------107
  Thread-3消费者****107
  Thread-0生产者------------108
  Thread-2消费者****108
  Thread-0生产者------------109
  Thread-2消费者****109
  Thread-1生产者------------110
  Thread-3消费者****110
로그인 후 복사

위 인쇄 결과


101 한 번 생산되고 두 번 소비됨

105 생산되었으나 소비되지 않음

이유 분석

두 개일 때 이는 두 개의 스레드가 생산자 생산 또는 소비자 소비를 동시에 수행하는데, 생산자가 있거나 두 개의 스레드가 있으면 wait()를 다시 통지()하는 이유는 한 스레드가 표시를 변경하고 다른 스레드가 직접 계속 실행하기 때문입니다. 판단 표시가 없는 것.

판단 표시가 한 번만 발생하면 실행해서는 안되는 스레드가 실행됩니다. 데이터 오류가 발생했습니다.

솔루션

while 판단 표시는 스레드가 실행 권한을 얻은 후 실행을 원하는지 여부에 대한 문제를 해결합니다. 즉, wait() 이후에 inform()이 실행될 때마다 표시가 나타납니다!

코드 개선(if->Resource에 있는 동안)

Resource.java

 
 /**
  * Created by yuandl on 2016-10-11./**
  * 资源
  */
  public class Resource {
  /*资源序号*/
  private int number = 0;
  /*资源标记*/
  private boolean flag = false;
  /**
  * 生产资源
  */
  public synchronized void create() {
  while (flag) {//先判断标记是否已经生产了,如果已经生产,等待消费;
  try {
  wait();//让生产线程等待
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  }
  number++;//生产一个
  System.out.println(Thread.currentThread().getName() + "生产者------------" + number);
  flag = true;//将资源标记为已经生产
  notify();//唤醒在等待操作资源的线程(队列)
  }
  /**
  * 消费资源
  */
  public synchronized void destroy() {
  while (!flag) {
  try {
  wait();
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  }
  System.out.println(Thread.currentThread().getName() + "消费者****" + number);
  flag = false;
  notify();
  }
  }
로그인 후 복사

문제가 다시 발견되었습니다


제작 후 74 등 특정 값으로 인쇄하면 프로그램이 잠긴 것처럼 멈춥니다.

원인 분석

알림: 스레드 하나만 깨울 수 있습니다. 이 파티가 이 파티를 깨우면 의미가 없습니다. 또한, while 판단 표시 + 통지는 "교착 상태"를 발생시킵니다.

솔루션

informAll은 자신의 스레드가 상대방의 스레드를 반드시 깨울 수 있는 문제를 해결합니다.

최종 코드 개선(Resource의 notify()->notifyAll())

Resource.java

 /**
  * Created by yuandl on 2016-10-11./**
  * 资源
  */
  public class Resource {
  /*资源序号*/
  private int number = 0;
  /*资源标记*/
  private boolean flag = false;
  /**
  * 生产资源
  */
  public synchronized void create() {
  while (flag) {//先判断标记是否已经生产了,如果已经生产,等待消费;
  try {
  wait();//让生产线程等待
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  }
  number++;//生产一个
  System.out.println(Thread.currentThread().getName() + "生产者------------" + number);
  flag = true;//将资源标记为已经生产
  notifyAll();//唤醒在等待操作资源的线程(队列)
  }
  /**
  * 消费资源
  */
  public synchronized void destroy() {
  while (!flag) {
  try {
  wait();
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  }
  System.out.println(Thread.currentThread().getName() + "消费者****" + number);
  flag = false;
  notifyAll();
  }
  }
로그인 후 복사

실행결과

Thread-0生产者------------412
  Thread-2消费者****412
  Thread-0生产者------------413
  Thread-3消费者****413
  Thread-1生产者------------414
  Thread-2消费者****414
  Thread-1生产者------------415
  Thread-2消费者****415
  Thread-0生产者------------416
  Thread-3消费者****416
  Thread-1生产者------------417
  Thread-3消费者****417
  Thread-0生产者------------418
  Thread-2消费者****418
  Thread-0生产者------------419
  Thread-3消费者****419
  Thread-1生产者------------420
  Thread-2消费者****420
로그인 후 복사

위 작업은 문제없이 완료되었습니다


관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!