Producer-Consumer模式

本文探讨了Producer-Consumer模式,通过一个制作蛋糕的例子展示了如何实现。文章介绍了Data、Producer、Consumer、Channel四个角色,重点讲解了在Java中如何使用synchronized关键字处理并发,并提到了wait、sleep和join方法及其与interrupt方法的交互。文中还分析了中断异常在不同方法中的处理方式,并预告了下一期将讨论Read-Write Lock模式。
摘要由CSDN通过智能技术生成

        这一章我们介绍Producer-Consumer模式。

        示例程序的场景如下:

  •  MakerThread制作蛋糕(String),并放置在桌子(Table)上;
  • 桌子上的蛋糕数量超过最大限额,MakerThread需进行等到;
  • EaterThread消费桌子(Table)的蛋糕;
  • 桌子上蛋糕数量为0时,EaterThread需进行等待;
  • MakerThread和EaterThread均有多个。

        话不多说,上链接(哦不,上代码^^)

import java.util.Random

public class MakerThread extends Thread{
    private final Random random;
    private final Table table;
    private static int id = 0;

    public MakerThread(String name, long seed, Table table){
        super(name);
        this.random = new Random(seed);
        this.table = table;
    }

    @Override
    public void run(){
        try{
            while(true){
                Thread.sleep(random.nextInt(1000));
                String cake = "[ Cake No." + nextId() + " by " + getName() + "] ";
                table.put(cake);
            }
        }catch(InterruptedException e){
            
        }
    }

    private static synchronized int nextId(){
        return id++;
    }
}
import java.util.Random;

public class EaterThread extends Thread{
    private final Random random;
    private Table table;

    public EaterThread(String name, long seed, Table table){
        super(name);
        this.random = new Random(seed);
        this.table = table;
    }

    @Override
    public void run(){
        try{
            while(true){
                String cake = table.take();
                Thread.sleep(random.nextInt(1000));
            }
        }catch(InterruptedException e){
        }        
    }
}
public class Table{
    private final String[] buffer;
    private int tail; //下次put的位置
    private int head; //下次take的位置
    private int count; //buffer中蛋糕的个数

    public Table(int count){
        this.buffer = new String[count];
        this.head = 0;
        this.tail = 0;
        this.count = 0;
    }

    public synchronized void put(String cake) throws InterruptedException{
        while(count >= buffer.length){
            wait();
        }
        System.out.println(Thread.currentThread().getName() + " puts" + cake);
        buffer[tail] = cake;
        tail = (tail + 1) % buffer.length;
        count++;
        notifyAll();
    }

    public synchronized String take throws InterruptedException(){
        while(count <= 0){
            wait();
        }
        String cake = buffer[head];
        head = (head + 1) % buffer.length;
        count--;        
        notifyAll();
        System.out.println(Thread.currentThread().getName() + " takes" + cake);
        return cake;
    } 
}
public class Main{
    public static void main(String[] args){
        Table table = new Table(3);
        new MakerThread("MakerThread-1", 31415, table).start();
        new MakerThread("MakerThread-2", 65831, table).start();
        new MakerThread("MakerThread-3", 97312, table).start();
        
        new EaterThread("EaterThread-1", 43813, table).start();
        new EaterThread("EaterThread-2", 63813, table).start();
        new EaterThread("EaterThread-3", 89323, table).start();
    }
}

        Producer-Consumer模式中包含了DataProducerConsumerChannel四种角色。Data由Producer产生,供Consumer消费,示例代码中为String类型的Cake蛋糕;Producer生产Data,并传递给Channel,由示例代码中的MakerThread扮演;Consumer从Channel中消费Data,由示例代码中的EaterThread扮演;Channel从Producer中获取Data,并接收Consumer的请求。由于可能存在多个Consumer或者Producer进程,Channel需要进行互斥处理(声明synchronized关键字)。当然,如果是单个Consumer或者Producer进程,则对应的函数无需声明为synchronized。类图如下所示:

        需要注意的是,我们在Table类中put方法和get方法都添加了中断异常(InterruptedException),典型添加中断异常的方法包括:wait方法、sleep方法和join方法。上诉三个方法均需要继续等待,wait方法等待notify/notifyAll;sleep方法等待时间片耗完;join方法等待其他线程终止。如果想在等待时间内提前终止(取消等待),那么interrupt方法便派上用场。

  • sleep方法和interrupt方法

        线程A执行了sleep方法:Thread.sleep(600000000);,如果想取消A的暂停状态,需要在其他线程中执行取消操作(因为A目前正处于暂停状态)。假设线程B执行下述语句:a.interrupt();变量a保存着线程A对应的Thread实例。interrupt方法是Thread的实例方法,任何线程均可以调用其他线程的interrupt方法,执行interrupt方法前不需要获取待取消线程的锁。interrupt方法被调用后,正在sleep的线程回终止暂停状态,并抛出interruptedException异常。

  • wait方法和interrupt方法

        对正在wait的线程执行interrupt方法,意即告诉该线程无需等待notify/notifyAll方法的调用,直接从等待队列中出来。假设A线程处于等待队列,B线程执行:a.interrupt();A也会抛出interruptedException异常。但和sleep、join方法不同的是,A线程需要先重新获得锁之后才会抛出interruptedException异常。

  • join方法和interrupt方法

        类似sleep方法,执行interrupt方法之后会立即到catch的语句中。

        这里需要注意的是,执行线程的interrupt方法不一定会抛出interruptedException,这是因为wait、sleep、join方法内部检查了线程的中断状态进而抛出interruptedException,而执行类似1+5; a=100;之类的操作就不会抛出异常而继续执行。如下所示:

         好啦,关于Producer-Consumer模式我们就介绍这么多,下一章介绍Read-Write Lock模式,欢迎大家继续阅读~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值