자바 다 중 스 레 드 병행 생산자 소비자 디자인 모델 사례 분석
수요 상황
두 개의 라인,하 나 는 생산 을 책임 지고 하 나 는 소 비 를 책임 지 며 생산 자 는 하 나 를 생산 하고 소비 자 는 하 나 를 소비한다.
문제 에 관련되다
4.567917.동기 화 문제:같은 자원 이 여러 스 레 드 에 동시 방문 할 때의 완전 성 을 어떻게 확보 합 니까?자주 사용 하 는 동기 화 방법 은 태그 나 잠 금 체 제 를 사용 하 는 것 이다4.567917.wait()/nofity()방법 은 기본 Object 의 두 가지 방법 으로 모든 자바 류 가 이 두 가지 방법 을 가지 고 있다 는 것 을 의미한다.그러면 우 리 는 모든 대상 에 게 동기 화 체 제 를 실현 할 수 있다wait()방법:버퍼 가 가득 차 거나 비어 있 을 때 생산자/소비자 스 레 드 는 자신의 실행 을 중단 하고 자 물 쇠 를 포기 하 며 자신 을 대기 상태 에 두 고 다른 스 레 드 를 실행 합 니 다
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()방법 을 호출 하면 된다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JPA + QueryDSL 계층형 댓글, 대댓글 구현(2)이번엔 전편에 이어서 계층형 댓글, 대댓글을 다시 리팩토링해볼 예정이다. 이전 게시글에서는 계층형 댓글, 대댓글을 구현은 되었지만 N+1 문제가 있었다. 이번에는 그 N+1 문제를 해결해 볼 것이다. 위의 로직은 이...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.