두 개의 스레드, 하나의 생산자 및 하나의 소비자
수요 시나리오
두 개의 스레드, 하나는 생산을 담당하고 다른 하나는 소비를 담당합니다.
관련 문제
동기화 문제: 여러 스레드가 동시에 액세스할 때 동일한 리소스의 무결성을 보장하는 방법. 일반적으로 사용되는 동기화 방법은 표시 또는 잠금 메커니즘을 사용하는 것입니다
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
위 작업은 문제없이 완료되었습니다