자바 다 중 스 레 드 병행 생산자 소비자 디자인 모델 사례 분석

1,2 개의 라인,1 명의 생산자,1 명의 소비자
수요 상황
두 개의 라인,하 나 는 생산 을 책임 지고 하 나 는 소 비 를 책임 지 며 생산 자 는 하 나 를 생산 하고 소비 자 는 하 나 를 소비한다.
문제 에 관련되다
4.567917.동기 화 문제:같은 자원 이 여러 스 레 드 에 동시 방문 할 때의 완전 성 을 어떻게 확보 합 니까?자주 사용 하 는 동기 화 방법 은 태그 나 잠 금 체 제 를 사용 하 는 것 이다4.567917.wait()/nofity()방법 은 기본 Object 의 두 가지 방법 으로 모든 자바 류 가 이 두 가지 방법 을 가지 고 있다 는 것 을 의미한다.그러면 우 리 는 모든 대상 에 게 동기 화 체 제 를 실현 할 수 있다wait()방법:버퍼 가 가득 차 거나 비어 있 을 때 생산자/소비자 스 레 드 는 자신의 실행 을 중단 하고 자 물 쇠 를 포기 하 며 자신 을 대기 상태 에 두 고 다른 스 레 드 를 실행 합 니 다
  • notify()방법:생산자/소비자 가 버퍼 에 제품 을 넣 거나 꺼 낼 때 다른 기다 리 는 스 레 드 에 실행 가능 한 통 지 를 보 내 고 자 물 쇠 를 포기 하여 자신 을 대기 상태 에 있 게 합 니 다
  • 코드 구현(총 세 가지 클래스 와 하나의 main 방법의 테스트 클래스)
    Resource.java
    
    package com.demo.ProducerConsumer;
    
    /**
     *   
     * @author lixiaoxi
     *
     */
    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
    
    package com.demo.ProducerConsumer;
    
    /**
     *    
     * @author lixiaoxi
     *
     */
    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
    
    package com.demo.ProducerConsumer;
    
    /**
     *    
     * @author lixiaoxi
     *
     */
    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
    
    package com.demo.ProducerConsumer;
    
    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();//     
    
      }
    }
    인쇄 결과:

    이상 의 인쇄 결 과 는 아무런 문제 가 없 음 을 알 수 있 습 니 다.
    2.여러 라인,여러 생산자 와 여러 소비자 의 문제
    수요 상황
    네 개의 라인,두 개 는 생산 을 책임 지고,두 개 는 소 비 를 책임 지고,생산 자 는 하 나 를 생산 하고,소비 자 는 하 나 를 소비한다.
    문제 에 관련되다
    notify All()방법:생산자/소비자 가 버퍼 에 제품 을 넣 거나 꺼 낼 때 다른 기다 리 는 모든 스 레 드 에 실행 가능 한 통 지 를 보 내 고 자 물 쇠 를 포기 하여 자신 을 대기 상태 에 있 게 합 니 다.
    재 테스트 코드
    ProducerConsumerTest.java
    
    package com.demo.ProducerConsumer;
    
    public class ProducerConsumerTest {
    
      public static void main(String args[]) {
        Resource resource = new Resource();
        new Thread(new Producer(resource)).start();//     
        new Thread(new Producer(resource)).start();//     
        new Thread(new Consumer(resource)).start();//     
        new Thread(new Consumer(resource)).start();//     
    
      }
    }
    실행 결과:


    이상 의 인쇄 결 과 를 통 해 문 제 를 발견 하 였 습 니 다.
    147 한 번 생산 하고 두 번 소비 했다.169 생산 했 지만 소 비 는 하지 않 았 다.
    원인 분석
    두 스 레 드 가 생산자 생산 이나 소비자 소 비 를 동시에 조작 할 때 생산자 나 소비자 의 두 스 레 드 가 모두 wait()일 경우 다시 notify()를 합 니 다.그 중의 한 스 레 드 가 표 시 를 바 꾸 었 기 때문에 다른 스 레 드 가 다시 아래로 직접 실 행 될 때 표 시 를 판단 하지 않 아서 발생 합 니 다.if 태그 판단,단 한 번,실행 하지 말 아야 할 스 레 드 가 실 행 될 수 있 습 니 다.데이터 오류 가 발생 했 습 니 다.
    해결 방안
    while 태그 판단,스 레 드 가 실행 권 을 가 져 온 후 실행 할 지 여부!wait()후 notify()할 때마다 표 시 를 다시 판단 하 는 것 이다.
    코드 개선(리 소스 의 if->while)
    Resource.java
    
    package com.demo.ProducerConsumer;
    
    /**
     *   
     * @author lixiaoxi
     *
     */
    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();
      }
    }
    실행 결과:

    문 제 를 재발 견 하 다
    어떤 값 으로 인쇄 되 었 습 니 다.예 를 들 어 187 을 생산 하고 프로그램 실행 카드 가 죽 었 습 니 다.마치 잠 겨 있 는 것 같 습 니 다.
    원인 분석
    notify:한 라인 만 깨 울 수 있 습 니 다.만약 에 우리 측 이 우리 측 을 깨 우 면 의미 가 없습니다.그리고 while 판단 태그+notify 는'잠 금'을 초래 할 수 있 습 니 다.
    해결 방안
    notify All 은 본 측의 스 레 드 가 반드시 상대방 의 스 레 드 문 제 를 깨 울 것 입 니 다.
    마지막 코드 개선(리 소스 의 notify()->notifyAll())
    Resource.java
    
    package com.demo.ProducerConsumer;
    
    /**
     *   
     * @author lixiaoxi
     *
     */
    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();
      }
    }
    실행 결과:

    이상 은 큰 성 과 를 거 두 었 으 니 아무런 문제 가 없다.
    전체 절 차 를 다시 정리 하 다.예 를 들 어 생산자 소비자 가 교체 운행 하고 매번 생산 후에 대응 하 는 소비자 가 있 습 니 다.테스트 류 는 인 스 턴 스 를 만 듭 니 다.생산자 가 먼저 실행 하고 run()방법 에 들 어가 create()방법 에 들 어가 면 flag 는 기본적으로 false,number+1 이 고 생산 자 는 하나의 제품 을 생산 합 니 다.flag 는 true 로 설정 하 는 동시에 notify All()방법 을 호출 하여 기다 리 고 있 는 모든 스 레 드 를 깨 웁 니 다.다음 에 생산자 가 운행 한다 면?이것 은 flag 가 true 이 고 while 순환 에 들 어가 wait()방법 을 실행 합 니 다.다음 에 소비자 가 실행 하면 destroy()방법 을 호출 합 니 다.이때 flag 는 true 입 니 다.소비자 가 제품 을 한 번 구 매 한 후에 flag 를 false 로 설정 하고 기다 리 고 있 는 모든 스 레 드 를 깨 웁 니 다.이것 이 바로 완전한 다 생산자 가 다 소비자 에 대응 하 는 문제 다.
    3.Lock 과 Condition 을 사용 하여 생산자 소비자 문 제 를 해결 합 니 다.
    위의 코드 에 문제 가 있 습 니 다.바로 우리 가 모든 스 레 드 가 기다 리 는 상 태 를 피하 기 위해 notify All 방법 으로 모든 스 레 드 를 깨 웠 습 니 다.즉,notify All 은 자기 측 과 상대방 의 스 레 드 를 깨 웠 습 니 다.만약 에 제 가 상대방 을 깨 우 는 스 레 드 만 필요 하 다 면 예 를 들 어 생산 자 는 소비자 의 스 레 드 만 깨 울 수 있 고 소비 자 는 생산자 의 스 레 드 만 깨 울 수 있 습 니 다.
    jdk 1.5 에서 다 중 스 레 드 업그레이드 솔 루 션 을 제공 합 니 다.
    1.동기 synchronized 를 Lock 작업 으로 바 꿉 니 다.
    2.Object 의 wait,notify,notify All 방법 을 Condition 대상 으로 교체 합 니 다.
    3.상대방 의 라인 만 깨 울 수 있다.
    전체 코드:
    Resource1.java
    
    package com.demo.ProducerConsumer;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     *   
     * @author lixiaoxi
     *
     */
    public class Resource1 {
    
      /*    */
      private int number = 0;
      /*    */
      private boolean flag = false;
      
      private Lock lock = new ReentrantLock();
      //  lock      condition  
      private Condition condition_pro = lock.newCondition(); 
      //  lock      condition  
      private Condition condition_con = lock.newCondition(); 
    
    
      /**
       *     
       */
      public void create() throws InterruptedException {
        
        try{
          lock.lock();
          //            ,      ,    
          while(flag){
            //     
            condition_pro.await();
          }
          //    
          number++;
          System.out.println(Thread.currentThread().getName() + "   ------------" + number);
          //          
          flag = true;
          //        ,        (      signalAll)
          condition_con.signal();
        }finally{
          lock.unlock();
        }
      }
    
      /**
       *     
       */
      public void destroy() throws InterruptedException{
    
        try{
          lock.lock();
          //            ,      ,    
          while(!flag){
            //     
            condition_con.await();
          }
          
          System.out.println(Thread.currentThread().getName() + "   ****" + number);
          //          
          flag = false;
          //        ,        
          condition_pro.signal();
        }finally{
          lock.unlock();
        }
      }
    }
    Producer1.java
    
    package com.demo.ProducerConsumer;
    
    /**
     *    
     * @author lixiaoxi
     *
     */
    public class Producer1 implements Runnable{
    
      private Resource1 resource;
    
      public Producer1(Resource1 resource) {
        this.resource = resource;
      }
    
      @Override
      public void run() {
        while (true) {
          try {
            Thread.sleep(10);
            resource.create();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
      
    }
    Consumer1.java
    
    package com.demo.ProducerConsumer;
    
    /**
     *    
     * @author lixiaoxi
     *
     */
    public class Consumer1 implements Runnable{
    
      private Resource1 resource;
    
      public Consumer1(Resource1 resource) {
        this.resource = resource;
      }
    
      @Override
      public void run() {
        while (true) {
          try {
            Thread.sleep(10);
            resource.destroy();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
      
    }
    ProducerConsumerTest1.java
    
    package com.demo.ProducerConsumer;
    
    public class ProducerConsumerTest1 {
    
      public static void main(String args[]) {
        Resource1 resource = new Resource1();
        new Thread(new Producer1(resource)).start();//     
        new Thread(new Producer1(resource)).start();//     
        new Thread(new Consumer1(resource)).start();//     
        new Thread(new Consumer1(resource)).start();//     
    
      }
    }
    실행 결과:

    총화
    1.생산자,소비자 가 모두 1 개 라면 flag 표 시 는 if 로 판단 할 수 있 습 니 다.여기에 여러 개가 있 으 니 반드시 while 로 판단 해 야 한다.
    2.while 판단 과 동시에 notify 함 수 는 이러한 스 레 드(예 를 들 어 한 소비자 가 다른 소비 자 를 깨 우 는 것)를 깨 울 수 있 습 니 다.이 로 인해 모든 소비자 들 이 바쁘게 기다 리 고 절 차 를 계속 수행 할 수 없습니다.notify All 함 수 를 사용 하여 notify 대신 이 문 제 를 해결 할 수 있 습 니 다.notify All 은 이러한 스 레 드 가 깨 어 나 지 않도록 보증 할 수 있 습 니 다.(소비자 스 레 드 는 생산자 스 레 드 를 깨 울 수 있 고 반대로 도 가능 합 니 다)바 쁜 대기 문 제 를 해결 할 수 있 습 니 다.
    가사 조심
    생산자/소비자 모델 이 최종 적 으로 달성 하 는 목적 은 생산자 와 소비자 의 처리 능력 을 균형 시 키 는 것 이다.이 목적 을 달성 하 는 과정 에서 한 명의 생산자 와 한 명의 소비자 만 을 요구 하지 않 는 다.여러 생산자 가 여러 소비자 에 게 대응 할 수 있 고 한 생산자 가 한 소비자 에 게 대응 할 수 있 으 며 여러 생산자 가 한 소비자 에 게 대응 할 수 있다.
    가짜 죽음 은 위의 세 가지 장면 에서 발생 한다.가사 란 모든 스 레 드 가 WAITING 상태 에 들 어가 면 프로그램 은 더 이상 업무 기능 을 수행 하지 않 고 전체 프로젝트 가 정체 상 태 를 보 이 는 것 을 말한다.
    예 를 들 어 생산자 A 와 생산자 B 가 있 고 버퍼 가 비어 소비자 들 은 WAITING 에 있다.생산자 B 는 웨 이 팅(WAITING)에 있 었 고,생산자 A 는 소비자 에 게 생산 통 지 를 받 았 으 며,생산자 A 가 생산 한 제품 은 소비자 에 게 알 렸 어야 했 는데 그 결과 생산자 B 에 게 알 렸 고,생산자 B 가 깨 어 나 완충 구역 이 가득 찬 것 을 발견 하고 웨 이 팅 을 계속 했다.이로써 두 생산자 의 라인 은 WAITING 에 있 고 소비 자 는 WAITING 에 있 으 며 시스템 은 가사 에 있다.
    위의 분석 을 통 해 알 수 있 듯 이 가사 가 발생 한 원인 은 notify 가 같은 종류 이기 때문에 단일 생산자/단일 소비자 가 아 닌 장면 은 두 가지 방법 으로 이 문 제 를 해결 할 수 있다.
    (1)synchronized 는 notifyAll()로 모든 스 레 드 를 깨 우 고 ReentrantLock 은 signal All()로 모든 스 레 드 를 깨 웁 니 다.
    (2)ReentrantLock 으로 두 개의 Condition,하 나 는 생산 자 를 나타 내 는 Condition,하 나 는 소비 자 를 나타 내 는 Condition,깨 울 때 해당 하 는 Condition 의 signal()방법 을 호출 하면 된다.
    이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

    좋은 웹페이지 즐겨찾기