Java生产者消费者问题专题

生产者消费者问题是多线程的经典问题,它描述有一个缓冲区作为仓库,生产者可以将产品放入仓库,消费者可以从仓库中取出商品。解决此类问题可以有两类方法:(1)采用某种机制保护生产者与消费者之间的同步。(2)在生产者与消费者之间建立管道

主要有四种方法:

(1)wait()/notify()方法

(2)await()/signal()方法

(3)BlockingQueue阻塞方法

(4)PipedInputStream/PipedOutputStream

wait()/notify()方法

这两个方法是Object的方法,这意味者所有的对象都有这两个方法,可以对任何对象实现同步。

wait()方法:当缓冲区已满/已空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等待状态,让其他线程执行。

notify()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他线程发出可执行的通知,同时放弃锁,使自己处理等待状态。

代码实现:

package produce;



import java.util.LinkedList;

import java.util.Queue;

import java.util.Random;



// 仓库

class Storage {

    // 仓库容量

    private final int MAX_SZIE = 100;

    // 商品存储队列载体

    private Queue<Object> list = new LinkedList<Object>();



    // 生产num个产品

    public void produce(int num) {

        // 同步代码段

        synchronized (list) {

            // System.out.println("i has be notify");

            while (list.size() + num > MAX_SZIE) { // 生产过剩

                System.out.println("[线程名]:" + Thread.currentThread().getName() + "\t[要生产的数量]:" + num + "\t[库存量]:"

                        + list.size() + "\t暂时不能执行任务");

                try {

                    list.wait(); // 等待条件满足

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

            for (int i = 0; i < num; i++) {

                list.offer(new Object());

            }

            System.out.println(

                    "[线程名]:" + Thread.currentThread().getName() + "\t[已生产产品数]:" + num + "\t[当前库存]:" + list.size());

            list.notifyAll(); // 唤醒等待线程

        }



    }



    // 消费num个产品

    public void consume(int num) {

        synchronized (list) {

            while (list.size() < num) {

                System.out.println("[线程名]:" + Thread.currentThread().getName() + "\t[要消费的数量]:" + num + "\t[库存量]:"

                        + list.size() + "\t暂时不能执行任务");

                try {

                    list.wait();

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

            for (int i = 0; i < num; i++) {

                list.poll();

            }

            System.out.println(

                    "[线程名]:" + Thread.currentThread().getName() + "\t[已消费产品数]:" + num + "\t[当前库存]:" + list.size());

            list.notifyAll();

        }

    }



}



// 生产者

class Producer extends Thread {

    // 每次生产的数量

    private int num;



    // 所放至的仓库

    private Storage storage;



    public Producer(Storage storage) {

        this.storage = storage;

    }



    @Override

    public void run() {

        while (true) {

            produce();

            try {

                Thread.sleep(new Random().nextInt(1000));

            } catch (InterruptedException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

        }

    }



    public void produce() {

        storage.produce(num);

    }



    public int getNum() {

        return num;

    }



    public void setNum(int num) {

        this.num = num;

    }



}



class Consumer extends Thread {

    // 每次消费产品数量

    private int num;

    // 所使用的仓库

    private Storage storage;



    public Consumer(Storage storage) {

        this.storage = storage;

    }



    @Override

    public void run() {

        while (true) {

            consume();

            try {

                Thread.sleep(new Random().nextInt(1000));

            } catch (InterruptedException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

        }

    }



    public void consume() {

        storage.consume(num);

    }



    public int getNum() {

        return num;

    }



    public void setNum(int num) {

        this.num = num;

    }



}



public class WaitNotify {



    public static void main(String[] args) throws InterruptedException {

        Storage storage = new Storage();



        Producer p1 = new Producer(storage);

        Producer p2 = new Producer(storage);

        Producer p3 = new Producer(storage);

        Producer p4 = new Producer(storage);



        Consumer c1 = new Consumer(storage);

        Consumer c2 = new Consumer(storage);

        Consumer c3 = new Consumer(storage);



        p1.setNum(10);

        p2.setNum(10);

        p3.setNum(10);

        p4.setNum(80);



        c1.setNum(50);

        c2.setNum(20);

        c3.setNum(70);



        p1.start();

        p2.start();

        p3.start();

        p4.start();

        c1.start();

        c2.start();

        c3.start();

    }



}

await()/signal()方法

JDK5以后,Java提供了更健状的多线程处理机制,可以实现更细粒度的多线程控制。await()/signal()是用来做同步的两种方法,它们与最新引入的Lock直接挂勾。通过Lock对象上调用newCondition()方法,将条件变量和一个对象进行绑定,从而保证并发程序访问竞争资源的安全。

代码实现:

“`java
package produce.signal;

import java.util.LinkedList;

import java.util.Queue;

import java.util.Random;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

// 仓库

class Storage {

// 仓库容量

private final int MAX_SZIE = 100;

// 商品存储队列载体

private Queue<Object> list = new LinkedList<Object>();



// 锁

private final Lock lock = new ReentrantLock();



// 仓库满时

private final Condition full = lock.newCondition();



// 仓库空时

private final Condition empty = lock.newCondition();



// 生产num个产品

public void produce(int num) {

    // 同步代码段

    lock.lock();

    while (list.size() + num > MAX_SZIE) { // 生产过剩

        System.out.println("[线程名]:" + Thread.currentThread().getName() + "\t[要生产的数量]:" + num + "\t[库存量]:"

                + list.size() + "\t暂时不能执行任务");

        try {

            full.await();

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

    for (int i = 0; i < num; i++) {

        list.offer(new Object());

    }

    System.out

            .println("[线程名]:" + Thread.currentThread().getName() + "\t[已生产产品数]:" + num + "\t[当前库存]:" + list.size());



    // 唤醒其他线程

    full.signalAll();

    empty.signalAll();



    // 释放锁

    lock.unlock();

}



// 消费num个产品

public void consume(int num) {

    lock.lock();

    while (list.size() < num) {

        System.out.println("[线程名]:" + Thread.currentThread().getName() + "\t[要消费的数量]:" + num + "\t[库存量]:"

                + list.size() + "\t暂时不能执行任务");

        try {

            // 由于库存不足,消费阻塞

            empty.await();

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

    for (int i = 0; i < num; i++) {

        list.poll();

    }

    System.out

            .println("[线程名]:" + Thread.currentThread().getName() + "\t[已消费产品数]:" + num + "\t[当前库存]:" + list.size());

    // 唤醒其他线程

    empty.signalAll();

    full.signalAll();

    // 释放锁

    lock.unlock();

}

}

// 生产者

class Producer extends Thread {

// 每次生产的数量

private int num;



// 所放至的仓库

private Storage storage;



public Producer(Storage storage) {

    this.storage = storage;

}



@Override

public void run() {

    while (true) {

        produce();

        try {

            Thread.sleep(new Random().nextInt(1000));

        } catch (InterruptedException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

    }

}



public void produce() {

    storage.produce(num);

}



public int getNum() {

    return num;

}



public void setNum(int num) {

    this.num = num;

}

}

class Consumer extends Thread {

// 每次消费产品数量

private int num;

// 所使用的仓库

private Storage storage;



public Consumer(Storage storage) {

    this.storage = storage;

}



@Override

public void run() {

    while (true) {

        consume();

        try {

            Thread.sleep(new Random().nextInt(1000));

        } catch (InterruptedException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

    }

}



public void consume() {

    storage.consume(num);

}



public int getNum() {

    return num;

}



public void setNum(int num) {

    this.num = num;

}

}

public class AWaitSignal {

public static void main(String[] args) throws InterruptedException {

    Storage storage = new Storage();



    Producer p1 = new Producer(storage);

    Producer p2 = new Producer(storage);

    Producer p3 = new Producer(storage);

    Producer p4 = new Producer(storage);



    Consumer c1 = new Consumer(storage);

    Consumer c2 = new Consumer(storage);

    Consumer c3 = new Consumer(storage);



    p1.setNum(10);

    p2.setNum(10);

    p3.setNum(10);

    p4.setNum(80);



    c1.setNum(50);

    c2.setNum(20);

    c3.setNum(70);



    p1.start();

    p2.start();

    p3.start();

    p4.start();

    c1.start();

    c2.start();

    c3.start();

}

}

“`

BlockingQueue阻塞队列

JDK5以后提供了同步队列BlockingQueue,其内部实现是上面的await()/signal()方法。阻塞操作是put()/get()方法。BlockingQueue在生成对象时就要指定大小。

put()方法:类似于上面的生产线程,达到最大容量自动阻塞。

get()方法:类似于上面的消费线程,容量为0时,自动阻塞。

“`java
package produce.blockingquue;

import java.util.Random;

import java.util.concurrent.BlockingQueue;

import java.util.concurrent.LinkedBlockingQueue;

// 仓库

class Storage {

// 仓库容量

private final int MAX_SZIE = 100;

// 商品存储队列载体

private BlockingQueue<Object> list = new LinkedBlockingQueue<Object>(MAX_SZIE);



// 生产num个产品

public void produce(int num) {

    // 同步代码段

    if (list.size() != MAX_SZIE) {

        for (int i = 0; i < num; i++) {

            try {

                list.put(new Object());

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

        System.out.println("[生产线程]:" + Thread.currentThread().getName() + "\t[当前库存:]" + list.size());

    } else {

        System.out.println("当前库存已满,不能再生产");

    }

}



// 消费num个产品

public void consume(int num) {

    if (list.size() != 0) {

        for (int i = 0; i < num; i++) {

            try {

                list.take();

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

        System.out.println("[生产线程]:" + Thread.currentThread().getName() + "\t[当前库存:]" + list.size());

    } else {

        System.out.println("当前库存为0,不能再消费");

    }

}

}

// 生产者

class Producer extends Thread {

// 每次生产的数量

private int num;



// 所放至的仓库

private Storage storage;



public Producer(Storage storage) {

    this.storage = storage;

}



@Override

public void run() {

    while (true) {

        produce();

        try {

            Thread.sleep(new Random().nextInt(1000));

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

}



public void produce() {

    storage.produce(num);

}



public int getNum() {

    return num;

}



public void setNum(int num) {

    this.num = num;

}

}

class Consumer extends Thread {

// 每次消费产品数量

private int num;

// 所使用的仓库

private Storage storage;



public Consumer(Storage storage) {

    this.storage = storage;

}



@Override

public void run() {

    while (true) {

        consume();

        try {

            Thread.sleep(new Random().nextInt(1000));

        } catch (InterruptedException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

    }

}



public void consume() {

    storage.consume(num);

}



public int getNum() {

    return num;

}



public void setNum(int num) {

    this.num = num;

}

}

public class BlockingQueueTest {

public static void main(String[] args) throws InterruptedException {

    Storage storage = new Storage();



    Producer p1 = new Producer(storage);

    Producer p2 = new Producer(storage);

    Producer p3 = new Producer(storage);

    Producer p4 = new Producer(storage);



    Consumer c1 = new Consumer(storage);

    Consumer c2 = new Consumer(storage);

    Consumer c3 = new Consumer(storage);



    p1.setNum(10);

    p2.setNum(10);

    p3.setNum(10);

    p4.setNum(80);



    c1.setNum(50);

    c2.setNum(20);

    c3.setNum(70);



    p1.start();

    p2.start();

    p3.start();

    p4.start();

    c1.start();

    c2.start();

    c3.start();

}

}


#wait()/notify()的使用原则

**用法**:

如果对象调用了wait(),就会使持有当前对象锁的线程移交对象锁,然后处理阻塞状态。这个方法必须在同步区域内部被调用,否则抛异常,这个同步区域将对象锁定在了调用wait方法的对象上。下面是wait方法使用的标准模式:

```java
synchronized(obj){

    while(<条件不满足>){

        obj.wait();    // 释放锁

    }

    // 满足条件后的操作 

}

应该始终使用while循环来调用wait方法,永远不要在循环之外调用wait方法。循环会在等待之前和之后测试条件是否满足,这样可以保持程序的活性安全性。如果不在循环内调用wait方法,而简单使用if语句可能导致其他线程调用notify/notifyAll,线程不会苏醒过来;也可能会导致,在条件不成立时,线程继续执行,破坏了被锁保护的约束关系。

notify唤醒的是单个等待的线程,notifyAll唤醒所有正在等待的线程。保守的建议是只使用notifyAll,这样能保证程序的正确性。这些线程醒过来后会检查它们正在等待的条件,如果发现条件并不满足,就会继续等待。

这里要注意几个事实:

(1)任何时刻,对象的控制权(monitor)只能被一个线程拥有。

(2)无论对象执行的是wait\notify\notifyAll方法,都必须保持当前线程取得了对象的控制权(monitor).。

(3)如果对象没有取得对象控制权就执行以上三个方法,就会报IllegalMonitorStateException异常。

(4)JVM是多线程的,不能保证运行时线程的时序性。

线程取得对象控制权的方法

(1)执行对象的某个同步实例方法。

(2)执行对象所在类的静态同步方法。

(3)执行对该对象加同步锁的同步块。

这里要特点注意,在同步块中不能更改同步对象的引用,也就是说不能重新赋值。

参考资料:

http://blog.csdn.net/monkey_d_meng/article/details/6251879

http://longdick.iteye.com/blog/453615

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值