Java线程-生产者和消费者问题

1.概述

对于算法—消费者和生产者模式大概是我们最常见的问题了。再此文章中,笔者给出了三种解决方式。

1.1 傻瓜式的银行案例
  1. BankCard类
public class BankCard {
    // 余额
    private double money;
    // 标记
    // flase没有钱,不能取,但可以存;
    // true有钱,可以取,但不能存
    private boolean flag;

    /**
     * 存钱
     * @param m 存款金额
     */
    public synchronized void save(double m){
        // 使用synchronizd使得操作原子化,防止了线程在使用过程中被优先级高的线程挤掉
        // 也预防了逻辑错误,即:还未存钱就有人来取钱 
        // 如果有钱看,等待
        while (flag){
            try {
                // 进入锁的等待队列等待,释放了cpu和锁
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.money = this.money + m;
        System.out.println(Thread.currentThread().getName() + "存了" + m + "余额是:" + this.money);
        // 修改标记
        this.flag = true;
        // 唤醒
        this.notifyAll();
    }

    public synchronized void take(double m){
        // 如果没钱,等待
        while (!flag){
            try {
                // 进入了锁的等待序列,释放了cpu和锁
                this.wait();
            } catch (Exception e){
                e.printStackTrace();
            }
        }
        this.money = this.money - m;
        System.out.println(Thread.currentThread().getName() + "取了" + m + "余额是:" + this.money);
        // 修改标记
        this.flag = false;
        // 唤醒
        this.notifyAll();
    }
}
  1. AddMoney(存钱类)
// 使用线程必须依赖Runnable接口
public class AddMoney implements Runnable{
    // 创建对象
    private BankCard bankCard;
    public AddMoney(BankCard bankCard){
        this.bankCard = bankCard;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++){
            // 每次存1000
            this.bankCard.save(1000);
        }
    }
}
  1. SubMoney(取钱类)
public class SubMoney implements Runnable{
    private BankCard bankCard;
    public SubMoney(BankCard bankCard){
        this.bankCard = bankCard;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++){
            this.bankCard.take(1000);
        }
    }
}
  1. TestBankCard(测试类)
public class TestBankCard {
    public static void main(String[] args){
        // 创建对象
        BankCard bankCard = new BankCard();
        // 创建存和取
        Runnable addMoney = new AddMoney(bankCard);
        Runnable subMoney = new SubMoney(bankCard);

        // 创建线程对象
        Thread saver = new Thread(addMoney,"saver");
        Thread taker = new Thread(subMoney,"taker");
        // 开启线程
        // 结果为:先存钱再取钱
        // 一存一取
        saver.start();
        taker.start();
    }
}
1.2 使用了synchronizad的生产者和消费者
  1. Bread类(实体类)
public class Bread {
    // 面包id
    private int id;
    // 面包的生产者姓名
    private String productor;
    public Bread(){}

    public Bread(int id, String productor) {
        this.id = id;
        this.productor = productor;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getProductor() {
        return productor;
    }

    public void setProductor(String productor) {
        this.productor = productor;
    }

    @Override
    public String toString() {
        return "Bread{" +
                "id=" + id +
                ", productor='" + productor + '\'' +
                '}';
    }
}
  1. BreadCon类(存取操作)
public class BreadCon {
    // 创建数组
    private Bread[] breads = new Bread[6];
    // 元素个数
    private int size;
    // 放入面包
    public synchronized void put(Bread b){
        // 数组满时
        while (size > 5){
            try {
                // 进入等待队列,释放cpu和锁
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 将面包放入数组容器中
        breads[size] = b;
        // 打印出信息
        System.out.println(Thread.currentThread().getName() + "生产了" + b.getId() + "号面包!");
        // 面包个数加一
        size += 1;
        // 唤醒消费者
        this.notifyAll();
    }

    public synchronized void take(){
        // 此时,容器内无面包,消费者不能取面包
        while (size <= 0){
            try {
                // 此时,线程进入等待队列,释放cpu和锁
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 面包数量减一
        size -= 1;
        // 将要取走的面包的信息赋予一个空的Bread类型的对象
        Bread bread = breads[size];
        // 将被取走的位置置为空
        breads[size] = null;
        System.out.println(Thread.currentThread().getName() + "取了" + bread.getId() + "生产者是:" + bread.getProductor());
        // 唤醒生产者
        this.notifyAll();
    }
}
  1. Consume(消费者类)
public class Consume implements Runnable{
    // 创建对象
    private BreadCon breadCon;

    public Consume(BreadCon breadCon){
        this.breadCon = breadCon;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++){
            breadCon.take();
        }
    }
}
  1. Productor(生产者类)
public class Productor implements Runnable{
    private BreadCon breadCon;
    public Productor(BreadCon breadCon){
        this.breadCon = breadCon;
    }
    @Override
    public void run() {
        for (int i = 0; i <10; i++){
            this.breadCon.put(new Bread(i,Thread.currentThread().getName()));
        }
    }
}
  1. Test(主方法类)
public class Test {
    public static void main(String[] args){
        BreadCon breadCon = new BreadCon();

        Runnable put = new Productor(breadCon);
        Runnable take = new Consume(breadCon);

        Thread productor1 = new Thread(put,"productor1");
        Thread productor2 = new Thread(put,"productor2");
        Thread consumer1 = new Thread(take, "consumer1");
        Thread consumer2 = new Thread(take, "consumer2");

        productor1.start();
        productor2.start();
        consumer1.start();
        consumer2.start();
    }
}
1.3 使用了锁队列的生产者和消费者模式
  1. Bread类:同上
  2. BreadCon类(存取方法类)
public class BreadCon {
    // 创建数组容器
    private Bread[] con = new Bread[6];
    // 元素个数
    private int size;
    // 创建Lock
    private Lock lock = new ReentrantLock();
    // 创建多个条件队列
    // 生产者队列
    private Condition proCondition = lock.newCondition();
    // 消费者对列
    private Condition conCondition = lock.newCondition();

    // 放入元素
    public void put(Bread bread){
        // 上锁
        lock.lock();
        try {
            // 布尔判断式或为:size > con.length - 1
            while (size > 5){
                try {
                    proCondition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(size + "------" + Thread.currentThread().getName());
            // 将面包放入数组容器
            con[size] = bread;
            // 面包个数加一
            size += 1;
            System.out.println(Thread.currentThread().getName() + "生产了:" + bread.getId() + "号面包!");
            // 唤醒所有
            conCondition.signal();
        } finally {
            // 解锁
            lock.unlock();
        }
    }

    // 取出面包
    public void take(){
        // 上锁
        lock.lock();
        try {
            while (size <= 0){
                try {
                    conCondition.await();
                } catch (Exception e){
                    e.printStackTrace();
                }
            }
            // 容量减一
            size -= 1;
            System.out.println(size + "----" + Thread.currentThread().getName());
            // 将面包放入Bread类型的空数组
            Bread b = con[size];
            // 将去除面包的位置置为空
            con[size] = null;
            System.out.println(Thread.currentThread().getName() + "取走了" + b.getId() + "号面包。生产者为:" + b.getProductor());
            // 唤醒生产者
            proCondition.signal();
        } finally {
            // 解锁
            lock.unlock();
        }
    }
}
  1. Test类(主方法类)
public class Test {
    public static void main(String[] args){
        BreadCon breadCon = new BreadCon();
        // 此步骤笔者对其进行了简化
        Runnable put = new Runnable() {
            @Override
            public void run() {
                for (int i =0; i < 10; i++){
                    breadCon.put(new Bread(i,Thread.currentThread().getName()));
                }
            }
        };
        Runnable take = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++){
                    breadCon.take();
                }
            }
        };

        Thread pro1 = new Thread(put,"pro1");
        Thread pro2 = new Thread(put,"pro2");
        Thread con1 = new Thread(take,"con1");
        Thread con2 = new Thread(take,"con2");

        pro1.start();
        pro2.start();
        con1.start();
        con2.start();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值