一个简单的生产者与消费者的多线程例子(一):简单的框架

生产者与消费者模式是一种常见的模式,出现在许多资源管理中。
在多线程中,生产者(MyProducer)与消费者(MyConsummer)之间共享一个产品管道(MyChannel),生产者make()的产品(Product)放入管道中,消费者consumer产品时从管道中取产品,他们之间的协同通过市场(MyMarket)管理。它们之间关系如下面用例图所示。代码附后。
用例图

生产者:不知道消费者是谁,更不知道消费者如何消费。它只与Channel打交道。

public abstract class MyProducer implements Runnable{
     protected final MyChannel myChannel;
     protected  MyProduct[] products;
    public MyProducer(MyChannel myChannel1){
        this.myChannel=myChannel1;
    }
     @Override
    public abstract  void run();
    protected abstract void make();
}

一个具体生产者(线程)

public class ASimpleProducer extends MyProducer {

    public ASimpleProducer(MyChannel myChannel1) {
        super(myChannel1);
    }

    @Override
    public void run() {// This only task is to put products in the channel
        while (true) {
            this.make();
            try {
                Thread.sleep(10);
            } catch (InterruptedException ex) {
                Logger.getLogger(ASimpleProducer.class.getName()).log(Level.SEVERE, null, ex);
            }
            for (MyProduct product : products) {
                myChannel.put(product);
            }
        }
    }

    @Override
    protected void make() {
        products = new ASimpleProduct[1];
        products[0] = new ASimpleProduct(Thread.class.getName());
    }

}

一个非线程的生产者

public class MyNoThreadProducer{
    private final SizeUnlimitedChannel pChannel;
    public MyNoThreadProducer(SizeUnlimitedChannel pChannel1){
        pChannel=pChannel1;
    }

    public void produce() {
        for (int i = 0;; i++) {
            ASimpleProduct product = new ASimpleProduct("" + i);
            pChannel.put(product);
            try {
                Thread.sleep(i);
            } catch (InterruptedException ex) {
                Logger.getLogger(MyMarketSizeUnlimited.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

消费者:不知道生产者是谁,更不要说生产者如何生产了。它只与Channel打交道

public abstract class MyConsumer extends Thread{
     protected final MyChannel myChannel;
     public MyConsumer(MyChannel myChannel1){
        this.myChannel=myChannel1;
    }
     @Override
    public abstract void run();
    protected abstract void consum(MyProduct product);
}

具体消费者

public class ASimpleConsumer extends MyConsumer {

    public ASimpleConsumer(MyChannel myChannel1) {
        super(myChannel1);
    }

    @Override
    public void run() {
        try {
            while (true) {
                Thread.sleep(50);
                consum(myChannel.take());
            }
        } catch (InterruptedException ex) {
            Logger.getLogger(ASimpleConsumer.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    protected void consum(MyProduct product) {
    }
}

产品

public interface MyProduct {

}

具体产品

public class ASimpleProduct implements MyProduct{
    String name;
    public ASimpleProduct(String myName){
        this.name=myName;
    }
    @Override
    public String toString(){
        return name;
    }
}

生产者与消费者通信的管道池
抽象管道池

public interface MyChannel {

    public void put(MyProduct product);
    public  MyProduct take();
}

有容量限制的管道池

public class SizeLimitedChannel implements MyChannel {
     private final MyProduct[] queue;
    private  int tail, head, count;

    public SizeLimitedChannel(int size1) {
        queue = new MyProduct[size1];
        this.head = 0;
        this.tail = 0;
        this.count = 0;
    }

    @Override
    public synchronized void put(MyProduct e) {
        try {
            while (count >= queue.length) {
                wait();
                //System.out.println(Thread.currentThread().getName()+"put() is waiting");
            }
            queue[count] = e;
            tail = (tail + 1) % queue.length;
            count++;
            //System.out.println(Thread.currentThread().getName()+"Put in one.Now the number of the products in the channel is:"+count);
            notifyAll();
        } catch (InterruptedException exception) {
        }
    }

    @Override
    public synchronized MyProduct take() {
        MyProduct e =null;
        try {
            while (count <= 0) {
                wait();
                //System.out.println(Thread.currentThread().getName()+"take() is waiting!");
            }
            e = queue[head];
            count--;
            //System.out.println(Thread.currentThread().getName()+"Take one out.Now the number of the products in the channel is:"+count);
            notifyAll();
        } catch (InterruptedException exception) {
        }
        return e;
    }
}

无容量限制的管道池

public class SizeUnlimitedChannel implements MyChannel {

    private final LinkedList<MyProduct> queue = new LinkedList();
    private int count = 0;

    @Override
    public synchronized void put(MyProduct e) {
        queue.addLast(e);
        this.recount(1);
        System.out.println("Put in one. count is:" + this.count);
        notifyAll();
    }

    @Override
    public synchronized MyProduct take() {
        MyProduct e = null;
        try {
            while (queue.size() <= 0) {
                wait();
            }
        } catch (InterruptedException ex) {
            Logger.getLogger(SizeUnlimitedChannel.class.getName()).log(Level.SEVERE, null, ex);
        }
        e = queue.removeFirst();
        recount(-1);
       // System.out.println("Take one away. the count is:" + this.count);
       if(this.count==0){
           System.out.println("The channel now is empty!");
       }
        return e;
    }

    public synchronized void recount(int i) {
        this.count = this.count + i;
    }
}

市场1:
Channel有容量限制。
生产者与消费者都是线程,都需要等待MyChannel的许可:对生产者来说,channel中产品的数量不大于容量;对消费者来说,channel中产品的数量不小于0。

public class MyMarketSizeLimited {
    public static void main(String[] args) {
        SizeLimitedChannel channel=new SizeLimitedChannel(5);
        ///////////////Producer//////////////////////
        new Thread(new ASimpleProducer(channel)).start();
        new Thread(new ASimpleProducer(channel)).start();
        ///////////////Consumer//////////////////
        new Thread(new ASimpleConsumer(channel)).start();
        new Thread(new ASimpleConsumer(channel)).start();
    }
}

市场2:
贪婪生产者:Channel无容量限制。
生产者不是线程,消费者是线程,产品池采用无容量限制的池。因此,只有消费者需要等待MyChannel的许可:channel中产品的数量不小于0。

public class MyMarketSizeUnlimited {

    public static void main(String[] args) {
        SizeUnlimitedChannel pChannel1 = new SizeUnlimitedChannel();
        MyNoThreadProducer pt = new MyNoThreadProducer(pChannel1);
        new Thread(new ASimpleConsumer(pChannel1)).start();
        pt.produce();
    }
}
  1. 角色及职责
    在这个模式中,共有5个角色:生产者(Producer)、消费者(Consumer)、产品通道(Channel)、产品(Product)、市场(Market)。其中,生产者和消费者是线程(Thread或Runnable,第2个例子中生产者不是线程)。Channel负责这两个线程的同步问题,同步的资源是存放Product的容器(List,Collection等)。
  2. 分工
    尽量让生产者与消费者线程实现可同步的,且不产生资源冲突的任务。
    当不可避免地出现资源冲突情况时,让Channel或Market或Product解决,而不是在两个线程(生产者和消费者)中解决,也不是在Market中解决(Market处理较顶层的内容为妥),更不是在Product中解决,唯Channel可以解决。
    为什么?
    (这个理由似乎不太妥)线程的目的是并行执行,而对于冲突的资源,需要用synchronized来保护,这是串行执行,因此,线程与synchronized的效果是相矛盾的,即在线程中不用synchronized。例如,当消费者负责向数据库的表(例如表A)中写入记录的任务时,如果某字段的值(如A.currentId)需要通过查询语句(从表B.id)获得,而被查询的表的对应的记录(表B.id)随时会改变(例如其他线程修改了其值,不再满足current条件了),这样就产生了前后不一致的情形。
    如果这个问题放在Channel中利用synchronized方法解决,由于其是单线程,比消费者可以更早地对产品相关属性进行处理。
    (这个理由较好)线程会有多个对象同时运行,而Channel则是唯一的(假设厨师和食客之间只摆一张桌子,则桌子就是Channel.注意是一张,不是二张,如果是两张桌子,则把两张桌子写到一个Channel中。) 对Channel来说,用synchronized限定的资源,由于它只有一个对象(唯一性),所以当有其它类的对象来访问synchronized的对象时,需要先获得锁。(如果有一个Channel类在程序中有两个对象,那么synchronized就没用了!想想两张桌子,对一张桌子的put进行了synchronized,对另一张桌子是无效的!)
    为什么synchronized的资源不放在除了Channel外的其他类中,因为其他类都可以有多个对象。各个对象会允许访问者使用自己的资源,另一个对象没有理由和权利去干涉。
    再想想,如果一个synchronized资源是通过private方法实现锁定,是否有意义?没有。因为private阻止外边的对象调用,只有自己在调用。如果只有一个对象,则一定不会冲突,如果有多个对象间接调用,则不能阻止冲突。
    再进一步,如果一个应用中,涉及到资源冲突,那么不能直接使别人写的现成的Channel,而必须自己写一个,除非别人已经把你要synchronized的内容给写好了。(应该惊讶一下:自己写Channel是必须的!没有自己的Channel,就无法synchronized!)
  3. 编写各个角色的顺序
    建议的顺序是先写producer,consumer,再写product,channel和market。先写producer和consumer,使这两个线程的功能尽量单一,避免不必要的混乱和错误。
  4. 关于线程数目
    生产者与消费者都是线程,为了有效利用CPU、内存、网络等资源,需要合理地设置线程数目。如果是计算线程,则线程数目要考虑CPU的核数,如果是持久化线程,则考虑数据库的连接池数目。
  5. 5.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值