实现生产者-消费者模型

143 阅读3分钟

问题介绍

  生产者消费者模型是经典的同步问题。问题大致如下:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权。因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去。因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态。然后等待消费者消费了商品,然后消费者通知生产者队列有空间了。同样地,当队列空时,消费者也必须等待,等待生产者通知它队列中有商品了。这种互相通信的过程就是线程间的协作。

下面总结了三种方式来实现一个消费者/生产者模型.

1.wait/notify模式

/**
 * wait and notify
 * 生产者和消费者问题
 *
 * @author bridge
 */
public class test1 {

    public static void main(String[] args) {
        Resource resource = new Resource();
        //生产者线程
        ProducerThread p = new ProducerThread(resource);
        //多个消费者
        ConsumerThread c1 = new ConsumerThread(resource);
        ConsumerThread c2 = new ConsumerThread(resource);
        ConsumerThread c3 = new ConsumerThread(resource);

        p.start();
        c1.start();
        c2.start();
        c3.start();
    }

}


class ProducerThread extends Thread {

    private Resource resource;

    public ProducerThread(Resource resource) {
        this.resource = resource;
        //setName("生产者");
    }

    public void run() {
        while (true) {
            try {
                Thread.sleep((long) (1000 * Math.random()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource.add();
        }
    }
}

class ConsumerThread extends Thread {
    private Resource resource;

    public ConsumerThread(Resource resource) {
        this.resource = resource;
        //setName("消费者");
    }

    public void run() {
        while (true) {
            try {
                Thread.sleep((long) (1000 * Math.random()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource.remove();
        }
    }
}


/**
 * 公共资源类
 */
class Resource {

    //当前资源数量
    private int num = 0;
    //资源池中允许存放的资源数
    private int size = 10;

    /***
     * 向资源池中添加资源
     */
    public synchronized void add() {

        if (num < size) {
            num++;
            System.out.println(Thread.currentThread().getName() + "生产一件资源,当前资源池有" + num + "个");
            notifyAll();
        } else {
            try {
                wait();
                System.out.println(Thread.currentThread().getName() + "线程进入等待");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 从资源池中取走资源
     */
    public synchronized void remove() {

        if (num > 0) {
            num--;
            System.out.println("消费者" + Thread.currentThread().getName() + "消耗一件资源," + "当前资源池有" + num + "个");
            notifyAll();
        } else {
            try {
                wait();
                System.out.println(Thread.currentThread().getName() + "线程进入等待");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

}

2.Lock condition模式

package ProducerAndConsumer.p2;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * lock和condition解法
 * 生产者和消费者问题
 *
 * @author bridge
 */
public class test2 {

    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition pCondition = lock.newCondition();
        Condition cCondition = lock.newCondition();

        Resource resource = new Resource(lock, pCondition, cCondition);

        //生产者线程
        ProducerThread p = new ProducerThread(resource);
        //多个消费者
        ConsumerThread c1 = new ConsumerThread(resource);
        ConsumerThread c2 = new ConsumerThread(resource);
        ConsumerThread c3 = new ConsumerThread(resource);

        p.start();
        c1.start();
//        c2.start();
//        c3.start();
    }
}

/**
 * 生产者线程
 */
class ProducerThread extends Thread {

    private Resource resource;

    public ProducerThread(Resource resource) {
        this.resource = resource;
        setName("生产者");
    }

    public void run() {
        while (true) {
            try {
                Thread.sleep((long) (1000 * Math.random()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource.add();
        }
    }
}

/**
 * 消费者线程
 */
class ConsumerThread extends Thread {

    private Resource resource;

    public ConsumerThread(Resource resource) {
        this.resource = resource;
        //setName("消费者");
    }

    public void run() {
        while (true) {
            try {
                Thread.sleep((long) (1000 * Math.random()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource.remove();
        }
    }
}


/**
 * 公共资源类
 */
class Resource {

    //当前资源数量
    private int num = 0;
    //资源池中允许存放的资源数
    private int size = 10;

    private Lock lock;
    private Condition pCondition;
    private Condition cCondition;

    public Resource(Lock lock, Condition pCondition, Condition cCondition) {
        this.lock = lock;
        this.pCondition = pCondition;
        this.cCondition = cCondition;

    }

    /***
     * 向资源池中添加资源
     */
    public void add() {

        lock.lock();
        try {
            if (num < size) {
                num++;
                System.out.println(Thread.currentThread().getName() + "生产一件资源,当前资源池有" + num + "个");
                //唤醒等待的消费者
                cCondition.signalAll();
            } else {
                //使生产者等待
                pCondition.await();
                System.out.println(Thread.currentThread().getName() + "线程进入等待");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }


    }

    /**
     * 从资源池中取走资源
     */
    public void remove() {

        lock.lock();
        try {
            if (num > 0) {
                num--;
                System.out.println("消费者" + Thread.currentThread().getName() + "消耗一件资源," + "当前资源池有" + num + "个");
                pCondition.signalAll();  //唤醒等待的生产者
            } else {
                try {
                    cCondition.await();
                    //使消费者等待
                    System.out.println(Thread.currentThread().getName() + "线程进入等待");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } finally {
            lock.unlock();
        }


    }

}

3.阻塞队列模式

package ProducerAndConsumer.p3;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * 阻塞队列解法
 * 生产者和消费者问题
 *
 * @author bridge
 */
public class test3 {

    public static void main(String[] args) {
        Resource resource = new Resource();
        //生产者线程
        ProducerThread p = new ProducerThread(resource);
        //多个消费者
        ConsumerThread c1 = new ConsumerThread(resource);
        ConsumerThread c2 = new ConsumerThread(resource);
        ConsumerThread c3 = new ConsumerThread(resource);

        p.start();
        c1.start();
        // c2.start();
        // c3.start();
    }

}


class ProducerThread extends Thread {

    private Resource resource;

    public ProducerThread(Resource resource) {
        this.resource = resource;
        //setName("生产者");
    }

    public void run() {
        while (true) {
            try {
                Thread.sleep((long) (1000 * Math.random()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource.add();
        }
    }
}

class ConsumerThread extends Thread {
    private Resource resource;

    public ConsumerThread(Resource resource) {
        this.resource = resource;
        //setName("消费者");
    }

    public void run() {
        while (true) {
            try {
                Thread.sleep((long) (1000 * Math.random()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource.remove();
        }
    }
}


/**
 * 公共资源类
 */
class Resource {


    private BlockingQueue resourceQueue = new LinkedBlockingQueue(10);

    /***
     * 向资源池中添加资源
     */
    public void add() {

        try {
            resourceQueue.put(1);
            System.out.println("生产者" + Thread.currentThread().getName() + "生产一件资源," + "当前资源池有" + resourceQueue.size() + "个资源");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 从资源池中取走资源
     */
    public void remove() {
        try {
            resourceQueue.take();
            System.out.println("消费者" + Thread.currentThread().getName() + "消耗一件资源," + "当前资源池有" + resourceQueue.size() + "个资源");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}