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();
}
}
- 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);
}
}
}
- 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);
}
}
}
- 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的生产者和消费者
- 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 + '\'' +
'}';
}
}
- 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();
}
}
- 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();
}
}
}
- 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()));
}
}
}
- 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 使用了锁队列的生产者和消费者模式
- Bread类:同上
- 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();
}
}
}
- 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();
}
}