Multi-Programming-9 非线程安全类实现生产者和消费者

来源:互联网 发布:gas mask mac 下载 编辑:程序博客网 时间:2024/06/05 03:59

1.生产者消费者问题

如果想看该问题详细描述,点击这里(不过是英文版本的)。
In computing, the producer–consumer problem[1][2] (also known as the bounded-buffer problem) is a classic example of a multi-process synchronization problem. The problem describes two processes, the producer and the consumer, who share a common, fixed-size buffer used as a queue. The producer’s job is to generate data, put it into the buffer, and start again. At the same time, the consumer is consuming the data (i.e., removing it from the buffer), one piece at a time. The problem is to make sure that the producer won’t try to add data into the buffer if it’s full and that the consumer won’t try to remove data from an empty buffer.
The solution for the producer is to either go to sleep or discard data if the buffer is full. The next time the consumer removes an item from the buffer, it notifies the producer, who starts to fill the buffer again. In the same way, the consumer can go to sleep if it finds the buffer to be empty. The next time the producer puts data into the buffer, it wakes up the sleeping consumer. The solution can be reached by means of inter-process communication, typically using semaphores. An inadequate solution could result in a deadlock where both processes are waiting to be awakened. The problem can also be generalized to have multiple producers and consumers.

生产者和消费者问题(也被叫做“有界缓冲区问题”)是一个经典的多线程同步问题的例子。生产者和消费者线程,共享一个共同的、固定大小的缓冲区,用作缓冲队列。
生产者的任务是产生数据,放入缓冲区,然后周而复始。同时,消费者消费数据(也就是从缓冲区移除一单位数据),每次消费一个单位。
问题在于:如何确保生产者不会再缓冲区满的时候添加数据;消费者不在缓冲区空的时候移除数据。
如何解决?
生产者:在缓冲区满的时候,要么休息,要么丢弃数据;当有消费者从缓冲区消费数据的时候,再唤醒生产者。
消费者:在缓冲区空的时候,休息;当生产者往缓冲区放数据的时候,唤醒消费者。
具体的解决方案:通过线程间通信解决,一般使用的是信号量机制(这里的术语均对应于操作系统,和Java中国年的Semaphore等有差异)。

  • 可能导致死锁的代码
int itemCount = 0;procedure producer() {    while (true) {        item = produceItem();        if (itemCount == BUFFER_SIZE) {            sleep();        }        putItemIntoBuffer(item);        itemCount = itemCount + 1;        if (itemCount == 1) {            wakeup(consumer);        }    }}procedure consumer() {    while (true) {        if (itemCount == 0) {            sleep();        }        item = removeItemFromBuffer();        itemCount = itemCount - 1;        if (itemCount == BUFFER_SIZE - 1) {            wakeup(producer);        }        consumeItem(item);    }}
    这里为何会死锁?

考虑:如消费者准备取数据的时候,itemCount为0,然后刚准备sleep(),结果被生产者中断,生产者生产了一个单位数据后,wakeup()消费者,然而此时消费者并没有sleep(),所以wakeup信号丢失(未起作用),之后消费者一直不回被唤醒(因为唤醒条件是itemCount==1),直至缓冲区满,生产者也去sleep(),结果生产者和消费者永远陷入sleep()状态,陷入死锁。

  • Semaphore代码
mutex buffer_mutex; // similar to "semaphore buffer_mutex = 1", but different (see notes below)semaphore fillCount = 0;semaphore emptyCount = BUFFER_SIZE;procedure producer() {    while (true) {        item = produceItem();        down(emptyCount);            down(buffer_mutex);                putItemIntoBuffer(item);            up(buffer_mutex);        up(fillCount);    }}procedure consumer() {    while (true) {        down(fillCount);            down(buffer_mutex);                item = removeItemFromBuffer();            up(buffer_mutex);        up(emptyCount);        consumeItem(item);    }}
  • monitor代码
monitor ProducerConsumer {    int itemCount;    condition full;    condition empty;    procedure add(item) {        while (itemCount == BUFFER_SIZE) {            wait(full);        }        putItemIntoBuffer(item);        itemCount = itemCount + 1;        if (itemCount == 1) {            notify(empty);        }    }    procedure remove() {        while (itemCount == 0) {            wait(empty);        }        item = removeItemFromBuffer();        itemCount = itemCount - 1;        if (itemCount == BUFFER_SIZE - 1) {            notify(full);        }        return item;    }}procedure producer() {    while (true) {        item = produceItem();        ProducerConsumer.add(item);    }}procedure consumer() {    while (true) {        item = ProducerConsumer.remove();        consumeItem(item);    }}

2.代码实现

package com.fqyuan.blog;import java.util.LinkedList;import java.util.List;import java.util.Random;public class ListProducerConsumer {    public static void main(String[] args) {        ListProducerConsumerUtil.demonstrate();    }}class ListProducerConsumerUtil {    public static void demonstrate() {        SharedObjectList sharedObjectList = new SharedObjectList();        Thread t1 = new Thread(new Runnable() {            @Override            public void run() {                try {                    sharedObjectList.produce();                } catch (InterruptedException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }            }        });        Thread t2 = new Thread(new Runnable() {            @Override            public void run() {                try {                    sharedObjectList.consume();                } catch (InterruptedException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }            }        });        t1.start();        t2.start();        try {            t1.join();            t2.join();        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}class SharedObjectList {    private List<Integer> list = new LinkedList<>();    private final int LIMIT = 10;    private Random random = new Random();    private Object lock = new Object();    private int cntValue = 0;    public void consume() throws InterruptedException {        while (true) {            synchronized (lock) {                while (list.size() == LIMIT)                    lock.wait();                list.add(cntValue++);                lock.notify();            }            Thread.sleep(random.nextInt(800));        }    }    public void produce() throws InterruptedException {        while (true) {            synchronized (lock) {                while (list.size() == 0)                    lock.wait();                System.out.print("list size is " + list.size());                int value = ((LinkedList<Integer>) list).removeFirst();                System.out.println(", and removed value is: " + value);                lock.notify();            }            Thread.sleep(random.nextInt(1000));        }    }}
运行结果:
list size is 1, and removed value is: 0list size is 1, and removed value is: 1list size is 1, and removed value is: 2list size is 1, and removed value is: 3list size is 2, and removed value is: 4list size is 2, and removed value is: 5list size is 3, and removed value is: 6list size is 2, and removed value is: 7list size is 2, and removed value is: 8list size is 3, and removed value is: 9list size is 2, and removed value is: 10list size is 1, and removed value is: 11list size is 2, and removed value is: 12list size is 1, and removed value is: 13list size is 2, and removed value is: 14list size is 4, and removed value is: 15list size is 3, and removed value is: 16list size is 5, and removed value is: 17list size is 7, and removed value is: 18list size is 6, and removed value is: 19list size is 7, and removed value is: 20list size is 6, and removed value is: 21list size is 6, and removed value is: 22list size is 7, and removed value is: 23list size is 7, and removed value is: 24

估计你也不想看这里.

阅读全文
0 0
原创粉丝点击