有很长一段时间想坚持写博客,但总是感觉自己的技术水平有限,所以迟迟没动手。今天我在写一个简单的生产者和消费者问题,在多线程的世界里绕了很久很久,但在回顾操作系统的知识时,其中的进程协同和互斥问题给我带来了很大的启发,尤其是其中的信号量Semaphore的通信方式,我很喜欢也很容易理解。在这个过程中,我对单向链表进行堆栈的模拟纠结了挺久,不是不会,而是上班中断断续续的思维以及生活和毕业后的种种感情掠夺了我很多的精力。下面先给出一段自己错误的单向链表的实现方式
一、单向链表加元素和取元素
首先有个产品类Product
public class Product {
private int curState; //这个属性没有使用,最初是想作为以后链表实现时进行产品读、取状态的标识
private Product next; //链表的指针
private int num; //记录当前元素的顺序
public Product(final int num) { //这里的变量很多都加了final修饰符,它的好处可以百度,目前我们公司的编码规范中就强调了这点。
this.num = num;
}
public int getNum() {
return this.num;
}
public int getCurState() {
return this.curState;
}
public void setCurState(final int curState) {
this.curState = curState;
}
public Product getNext() {
return this.next;
}
public void setNext(final Product next) {
this.next = next;
}
}
然后我们给出一个单向链表的实现
public class ProductBuffer_error {
private Product header; //头
private Product tailer; //尾
private int size; //大小
private int index; //记录当前存取位置
public ProductBuffer_error(final int size) {
this.size = size;
}
public Product getHeader() {
return this.header;
}
public Product getTailer() {
return this.tailer = tailer;
}
private void setHeader(final Product header) {
this.header = header;
}
private void setTailer(final Product tailer) {
this.tailer = tailer;
}
/**
* 这样进行实现有很大的问题,因为当我们进行链表的后向插入时,每次setTailer都可能缩短链表的实际大小
*/
public void store(final Product product) {
Product header = this.getHeader();
Product tailer = this.getTailer();
if(header == null) { //首次赋值,应该如何做?不言而喻
product.setNext(product);
this.setHeader(product);
this.setTailer(product);
this.index = 1;
} else {
if(index !=0 ) { //为啥要判断index的值,可以考虑!
product.setNext(this.getHeader());
tailer.setNext(product);
this.setTailer(product);
index = ++index % this.size;
} else {
product.setNext(header.getNext());
tailer.setNext(product);
this.setHeader(product);
this.index = 1;
}
}
}
/**
*这样取也有问题,虽然每次都是取header,但是index的值是和store共用的,相互之间都有影响。
*/
public Product take() {
Product header = this.getHeader();
if(header!= null) {
this.getTailer().setNext(header.getNext());
this.setHeader(header.getNext());
/*#ERROR: this.index = ++this.index % this.size;
if(this.index == 0 ) {
this.setHeader(null);
this.setTailer(null);
}*/
}
return header;
}
/**
* 检索元素的位置
*/
public Product indexOf(final int index) {
int count = index % this.size;
int loc = 0;
Product item = this.getHeader();
while(item != null && loc != count) {
item = item.getNext();
loc = ++loc % this.size;
}
return loc == count ? item : null;
}
}
二、整个完整的内容代码
ProductBuffer
/**
* 通过单向循环链表实现固定大小的堆栈.
*/
public class ProductBuffer
{
private Product header; //头部
private Product tailer; //尾部
private int size; //链表大小
private int index; //当前位置
private boolean canW = true; //是否可写
private boolean canR = false; //是否可读
public ProductBuffer(int paramInt)
{
this.size = paramInt;
}
public Product getHeader() {
return this.header;
}
public Product getTailer() {
return (this.tailer = this.tailer);
}
public void setHeader(Product paramProduct) {
this.header = paramProduct;
}
public void setTailer(Product paramProduct) {
this.tailer = paramProduct;
}
public void setSize(final int size) {
this.size = size;
}
public int getSize() {
return this.size;
}
public void setIndex(final int index) {
this.index = index;
}
public int getIndex() {
return this.index;
}
//加一个信号量,用于判断当前链表是否可以进行读、写操作
public boolean canWrite() {
return this.canW;
}
public boolean canRead() {
return this.canR;
}
public void setWrite(final boolean canW) {
this.canW = canW;
}
public void setRead(final boolean canR) {
this.canR = canR;
}
//查看当前链表的整体状态,很有帮助
public void print() {
Product localProduct = getHeader();
int i = 0;
while (i < this.size) {
System.out.print(localProduct.getNum() + "\t");
localProduct = localProduct.getNext();
++i;
}
System.out.println();
}
}
生产者
/**
* 生产者.
*/
public class Producer {
private ProductBuffer productBuffer;
public Producer(final ProductBuffer productBuffer) {
this.productBuffer = productBuffer;
}
public ProductBuffer getProductBuffer() {
return this.productBuffer;
}
/**
* 生产
*/
public synchronized void produce(final int num) {
Product header = new Product(num);
this.store(header);
}
/**
* 存储元素
*/
public synchronized void store(Product paramProduct) {
Product header = productBuffer.getHeader();
int index = productBuffer.getIndex();
final int size = productBuffer.getSize();
if (header == null) {
paramProduct.setNext(paramProduct);
productBuffer.setHeader(paramProduct);
productBuffer.setTailer(paramProduct);
productBuffer.setIndex(1);
} else {
Product tailer = productBuffer.getTailer();
if (index < size) {
paramProduct.setNext(header);
tailer.setNext(paramProduct);
productBuffer.setTailer(paramProduct);
productBuffer.setIndex(++index);
productBuffer.setRead(true);
} else {
productBuffer.setWrite(false);
}
}
}
public synchronized void PProduce() {
if(!productBuffer.canWrite()) {
try {
this.wait();
} catch(Exception e) {
e.printStackTrace();
}
}
}
public synchronized void VProduce() {
if(productBuffer.canWrite()) {
this.notify();
}
}
}
消费者
/**
* 消费者.
*/
public class Customer {
private ProductBuffer productBuffer;
public Customer(final ProductBuffer productBuffer) {
this.productBuffer = productBuffer;
}
public ProductBuffer getProductBuffer() {
return this.productBuffer;
}
/**
* 消费.
*/
public Product consume() {
return this.take();
}
/**
* 取元素.
*/
public synchronized Product take() {
Product product = productBuffer.getHeader();
int index = productBuffer.getIndex();
if (product != null) {
Product second = product.getNext();
productBuffer.getTailer().setNext(second);
productBuffer.setHeader(second);
productBuffer.setIndex(--index);
productBuffer.setWrite(true);
}
if (index < 1) {
productBuffer.setHeader(null);
productBuffer.setTailer(null);
productBuffer.setRead(false);
}
return product;
}
public synchronized void PConsume() {
if(!productBuffer.canRead()) {
try {
this.wait();
} catch(Exception e) {
e.printStackTrace();
}
}
}
public synchronized void VConsume() {
if(productBuffer.canRead()) {
this.notify();
}
}
}
测试类
public class PCTest {
public static void main(String[] args) {
final ProductBuffer buffer = new ProductBuffer(8); //先来一个缓冲区
final Producer producer = new Producer(buffer); //再来一个生产者
final Customer customer = new Customer(buffer); //再来一个消费者
Thread producerThread = new Thread(new Runnable() {
public void run() {
int i = 0;
while(++i <= 100) {
producer.PProduce();//用前先询问权限
producer.produce(i);
System.out.println("生产了第 " + i + "个产品");
customer.VConsume(); //用后记得通知
}
}
});
Thread customerThread = new Thread(new Runnable() {
public void run() {
int i = 0;
while(++i <= 100) {
customer.PConsume(); //用前询问权限
Product product = customer.consume();
System.out.println("消费了第 " + product.getNum() + "个产品");
producer.VProduce(); //用后记得通知
}
}
});
customerThread.start();
producerThread.start ();
}
}
说明:
1、上面的代码其实设计到了四个参与者,Product、ProductBuffer、Producer和Customer。
2、核心是ProductBuffer这个缓存区如何实现,可以列表、链表甚至可以是树,我觉得什么数据结构无所谓,主要是记住判断它的状态,也就是满和空这两种。
3、Producer和Customer这两个实际使用者,其他他们的工作内容很简单,也就是存和取,但是难点在于如何进行协同。如果看过操作系统的进程同步,那么对于基于信号量的同步方式应该可以很简单的运用于线程。只要记住下面的模型即可:
A; V(s); 线程1
P(s); B; 线程2
这是对于同一个资源的先后顺序。
如果有两个信号量,也就是类似于本文的生产者和消费者问题,也是有公式的,如下
P(s1); A; V(s2); 线程1
P(s2); B; V(s1); 线程2
这样相互进行通知和自阻塞的协同通信其实没那么难,关键是分析实际情况中协同信号量的个数和他们的先后关系才是难点。而P、V这样的函数,在操作系统找那个称为原语,其实就是类似于线程中所讲的原子性问题,用起来就是加锁。