简述:
描述了生产者-消费者问题, 使用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}
显然与要求违背.
总结:
这仅仅是一行代码的临界区问题就出现了容易忽视的问题, 说明多线程编程有难度, 但是他却很有必要.