java生产消费者算法

 

今天的db2培训仍然没有太多的实践,围绕考试来的,闲着也是闲着,想到我们后续业务中使用多线程的地方比较多。于是想起那个经典的案例:生产者和消费者

关于这个案例的原理,就不多说了。主要涉及到临界资源互斥锁的使用、wait和notify操作,还有就是线程sleep。关于几个操作的区别,我会写在代码的注释中。这和我的工作习惯有关系,不喜欢写文档(敏捷开发认为代码是最好的文档,^_^,我的代码没有重构,完成了功能就贴上来了,当然不符合敏捷的要求了^_^,见谅)

请看代码:

1、生产和消费的产品抽象类

//预留
public abstract class Product {

    public String name;
    public abstract String toString();

}

2、一个具体的产品类

public class AProduct extends Product {

    public AProduct(String pname) {
        super();
        name = pname;
        // TODO Auto-generated constructor stub
    }

    public String toString() {
        // TODO Auto-generated method stub
        return name;
    }
}

 

3、容器类(仓库)

import java.util.ArrayList;

/*

 * 存放生产者和消费者的产品队列

 * */
public class Container {

    private ArrayList arrList = new ArrayList();
    private int LENGTH = 10;

    public boolean isFull() {
        return arrList.size() == LENGTH;
    }

    public boolean isEmpty() {
        return arrList.isEmpty();
    }

    /* 如果此处不加synchronized锁,那么也可以再调用push的地方加锁
    * 既然此处加了锁,那么再别的地方可以不加锁
    */
    public synchronized void push(Object o) {
        arrList.add(o);
    }

    // 如果此处不加synchronized锁,那么也可以再调用push的地方加锁
    public synchronized Object pop() {
        Object lastOne = arrList.get(arrList.size() - 1);
        arrList.remove(arrList.size() - 1);
        return lastOne;
    }
}




 

4、休息一会,生产者和消费者都要休息,因此作为抽象基类

public abstract class Sleep {

    public void haveASleep() throws InterruptedException {
        Thread.sleep((long) (Math.random() * 3000));
    }

}

 

// sleep让出cpu时间给其他线程执行.也许你会问,既然我已经wait了,为什么还要sleep?
 // wait()的作用就是阻塞当前线程并且释放对象的锁给别人(一般是等待队列中的第一个线程)
 // Thead.sleep()也是阻塞当前线程,但不释放锁。
 // sleep()方法是使线程停止一段时间的方法。
 // 在sleep 时间间隔期满后,线程不一定立即恢复执行。
 // 这是因为在那个时刻,其它线程可能正在运行而且没有被调度为放弃执行,除非 (a)“醒来”的线程具有更高的优先级。
 // (b)正在运行的线程因为其它原因而阻塞。 wait()是线程交互时,如果线程对一个同步对象x
 // 发出一个wait()调用,该线程会暂停执行,被调对象进入等待状态,直到被唤醒或等待时间到。
 // 当调用wait()后,线程会释放掉它所占有的“锁标志”,从而使线程所在对象中的其它synchronized数据可被别的线程使用。
 // waite()和notify()因为会对对象的“锁标志”进行操作,所以它们必须在synchronized函数或synchronized

 

/*
 * 消费者线程
 **/

public class Consumer extends Sleep implements Runnable {

    private Container contain = null;

    public Consumer(Container contain) {
        super();
        this.contain = contain;
    }

    public void run() {
        // TODO Auto-generated method stub
        while (true) {
            synchronized (contain) {
                while (contain.isEmpty()) {
                    try {
                        contain.wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
            consume();//消费
            try {
                haveASleep();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            synchronized (contain) {
                contain.notify();
            }
        }
    }

    private void consume() {
        Product a = (AProduct) contain.pop();
        System.out.println("消费了一个产品" + a.toString());
    }
}

 

/*

 * 生产者线程

 * */

public class Producator extends Sleep implements Runnable {

    private Container contain = null;

    public Producator(Container contain) {
        super();
        this.contain = contain;
    }

    public void run() {
        // TODO Auto-generated method stub
        while (true) {
            synchronized (contain) {
                while (contain.isFull()) {
                    try {
                        contain.wait();// 阻塞当前线程,当前线程进入等待队列。这个时候只有等待别的线程来唤醒自己了。
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
            producer();// 生产一个产品
            try {
                haveASleep();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            synchronized (contain) {
                contain.notify();// 唤醒等待队列中正在等待的第一个线程,让其执行。
            }
        }
    }

    public void producer() {
        Product aProduct = new AProduct("pp:" + String.valueOf(Math.random()));
        System.out.println("生产了一个产品:" + aProduct.toString());
        contain.push(aProduct);
    }
}

 

 

 

5、写一个测试吧:

public class TestMain {

    /**
     * @param args
     */

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Container contain = new Container();
        Producator p = new Producator(contain);
        Consumer c = new Consumer(contain);
        Thread pt = new Thread(p);
        Thread ct = new Thread(c);
        pt.start();
        ct.start();
    }
}

 

好了,看看执行结果吧:

生产了一个产品:pp:0.5010546528255287

生产了一个产品:pp:0.08173509855014827

消费了一个产品pp:0.5010546528255287

生产了一个产品:pp:0.7022996270428004

生产者与消费者问题算法实现》 设计思想 因为有多个缓冲区,所以生产者线程没有必要在生成新的数据之前等待最后一个数据被消费者线程处理完毕。同样,消费者线程并不一定每次只能处理一个数据。在多缓冲区机制下,线程之间不必互相等待形成死锁,因而提高了效率。   多个缓冲区就好像使用一条传送带替代托架,传送带上一次可以放多个产品。生产者在缓冲区尾加入数据,而消费者则在缓冲区头读取数据。当缓冲区满的时候,缓冲区就上锁并等待消费者线程读取数据;每一个生产或消费动作使得传送带向前移动一个单位,因而,消费者线程读取数据的顺序和数据产生顺序是相同的。 可以引入一个count计数器来表示已经被使用的缓冲区数量。用hNotEmptyEvent 和hNotFullEvent 来同步生产者和消费者线程。每当生产者线程发现缓冲区满( count=BufferSize ),它就等待hNotEmptyEvent 事件。同样,当消费者线程发现缓冲区空,它就开始等待hNotEmptyEvent。生产者线程写入一个新的数据之后,就立刻发出hNotEmptyEvent 来唤醒正在等待的消费者线程;消费者线程在读取一个数据之后,就发出hNotFullEvent 来唤醒正在等待的生产者线程。 程序的设计思想大致为:设置一while循环,pi生产者访问临界区,得到权限访问缓冲区,如果缓冲区满的,则等待,直到缓冲区非满;访问互斥锁,当得到互斥锁且缓冲区非满时,跳出while循环,开始产生新数据,并把数据存放于Buffer缓冲区中,当数据存放结束则结束临界区;接着唤醒消费者线程;ci消费者访问临界区,得到权限访问缓冲区,如果缓冲区为空,没有可以处理的数据,则释放互斥锁且等待,直到缓冲区非空;当等到缓冲区非空时,跳出while循环;消费者获得数据,并根据所获得的数据按类别消费(当消费者获得的数据为大写字母时,则把大写字母转换成小写字母,并显示;当消费者获得的数据为小写字母时,则把小写字母转换成大写字母,并显示;当消费者获得的数据为字符0、1、2、……8、9时,把这些字符直接显示到屏幕;当消费者获得的数据为符号(+、-、*、\……)时,把这些符号打印成7行7列的菱形);处理完数据后,结束临界区;接着唤醒生产者线程。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值