进程同步--生产者消费者问题(Producer-consumer Problem)
简述:
描述了生产者-消费者问题, 使用java代码重现了较为难以理解的问题, 根据机器代码执行和CPU调度原理分析问题出现的原因.
From Wikipedia
生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
Java code
public class MultiThread { //多个线程共享这一个变量 private static int counter = 0; //最大的尺寸 private static final int AMX_SIZE=5; static class Consumer implements Runnable{ @Override public void run() { while(true){ while (counter ==0){ //System.out.println("c empty"); } counter -= 1; System.out.println(" c counter="+ counter); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } } static class Producer implements Runnable{ @Override public void run() { while(true){ while (counter ==AMX_SIZE){ //System.out.println("p full"); } counter += 1; System.out.println("p counter="+ counter); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { new Thread(new Consumer()).start(); new Thread(new Producer()).start(); } }
分析: 这样的代码看似解决了问题, 但是实际上还隐含有问题, 问题在对于itemCount改变的代码. 首先, 考虑java代码的机器代码如下:
//假定现在counter = 5 //counter++的机器代码 step A1: register1 = counter step B1: register1 = register1 + 1 step C1: counter = register1 //counter--的机器代码 step A2: register2 = counter step B2: register2 = register2 - 1 step C2: counter = register2
现在, 我们需要求得有多少种执行可能顺序, 这个问题转化为:
已知有A1B1C1和A2B2C2两个有序数组, 现在求他们的全排列, 要求是他们的排列中, 两个数组仍然有序.
其中一种情况如下所示:
execute step A1: register1 = counter {counter = 5} execute step B1: register1 = register1 + 1 {counter = 6} execute step A2: register2 = counter {counter = 5} execute step B2: register2 = register2 - 1 {counter = 4} execute step C1: counter = register1 {counter = 6} execute step C2: counter = register2 {counter = 4}
显然与要求违背.
总结: 这仅仅是一行代码的临界区问题就出现了容易忽视的问题, 说明多线程编程有难度, 但是他却很有必要.
上一篇:
IDEA上Java项目控制台中文乱码