第一种:生产者和消费者(使用synchronized 锁)
此方法的特点:因为synchronized 锁只能创建一个队列,因此在唤醒其他线程的时候必须要唤醒所有的线程,才能保证生产者和消费者交替运行。代码实现如下;
Bread类
package com.qf.gp2002_2;
/*
* wgy
* 2020/8/5
* 11:30
*/
public class Bread {
private int id;
private String productName;
public Bread(int id, String productName) {
this.id = id;
this.productName = productName;
}
public Bread() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
@Override
public String toString() {
return "Bread{" +
"id=" + id +
", productName='" + productName + '\'' +
'}';
}
}
BreadCon类
package com.qf.gp2002_2;
/*
* wgy
* 2020/8/5
* 11:31
*/
public class BreadCon {
//数组
private Bread[] cons=new Bread[6];
//元素个数
private int size;
//放入面包
public synchronized void put(Bread b){
while(size>5){
//等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
cons[size]=b;
System.out.println(Thread.currentThread().getName()+"生产了"+b.getId()+"号面包");
size++;
//唤醒消费者
this.notifyAll();
}
//取走面包
public synchronized void take(){
while(size<=0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
size--;
Bread b=cons[size];
cons[size]=null;
System.out.println(Thread.currentThread().getName()+"消费了"+b.getId()+"号面包,生产者:"+b.getProductName());
//唤醒生产者
this.notifyAll();
}
}
生产者(Produce类)
public class Produce implements Runnable{
private BreadCon con;
public Produce(BreadCon con){
this.con=con;
}
@Override
public void run() {
for(int i=0;i<100;i++){
con.put(new Bread(i, Thread.currentThread().getName()));
}
}
}
消费者(Consume类)
public class Consume implements Runnable {
private BreadCon con;
public Consume(BreadCon con) {
this.con = con;
}
@Override
public void run() {
for(int i=0;i<100;i++){
con.take();
}
}
}
测试类(TestBread类)
public class TestBread {
public static void main(String[] args) {
//面包容器
BreadCon breadCon=new BreadCon();
//创建生产和消费
Produce produce=new Produce(breadCon);
Consume consume=new Consume(breadCon);
//创建线程对象
Thread zhuxi=new Thread(produce, "铸玺");
Thread bingbing=new Thread(consume,"冰冰");
Thread zhuxi2=new Thread(produce, "铸玺2");
Thread bingbing2=new Thread(consume,"冰冰2");
//启动
zhuxi.start();
bingbing.start();
zhuxi2.start();
bingbing2.start();
}
}
第二种:生产者和消费者(使用Lock锁中的Condition接口)
此方法的特点:因为Condition接口具有创建多队列的能力,因此可以将生产者和消费者分别放入不同的队列中,因此在唤醒线程的时候可以指定唤醒消费者或者是生产者,不用唤醒全部线程就能够实现生产者和消费者交替运行的效果。代码实现如下:
Bread类
public class Bread {
private int id;
private String prouductName;
public Bread(int id, String prouductName) {
this.id = id;
this.prouductName = prouductName;
}
public Bread() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProuductName() {
return prouductName;
}
public void setProuductName(String prouductName) {
this.prouductName = prouductName;
}
@Override
public String toString() {
return "Bread{" +
"id=" + id +
", prouductName='" + prouductName + '\'' +
'}';
}
}
BreadCon类
public class BreadCon {
//数组容器
private Bread[] con=new Bread[6];
//元素个数
private int size;
//创建Lock
private Lock lock=new ReentrantLock();
//创建条件队列
//Condition condition=lock.newCondition();
//创建多个条件队列
//生产者队列
private Condition proCondition=lock.newCondition();
//消费者队列
private Condition conCondition=lock.newCondition();
//放入元素
public void put(Bread b){
//上锁
lock.lock();
try {
while (size > con.length-1) {
try {
proCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(size+"-----------------"+ Thread.currentThread().getName());
con[size] = b;
size++;
System.out.println(Thread.currentThread().getName() + "生产了" + b.getId() + "号面包");
//唤醒所有
conCondition.signal();
}finally {
//解锁
lock.unlock();
}
}
//取出面包
public void take(){
//上锁
lock.lock();
try {
while(size <= 0) {
try {
conCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
size--;
System.out.println(size+"-----------------"+ Thread.currentThread().getName());
Bread b = con[size];
con[size] = null;
System.out.println(Thread.currentThread().getName() + "消费了" + b.getId() + "号面包,生产者:" + b.getProuductName());
proCondition.signal();
}finally {
//解锁
lock.unlock();
}
}
}
测试
public class TestBread {
public static void main(String[] args) {
//创建容器
BreadCon con=new BreadCon();
//生产对象
Runnable produce=new Runnable() {
@Override
public void run() {
for(int i=0;i<10;i++){
con.put(new Bread(i, Thread.currentThread().getName()));
}
}
};
//消费对象
Runnable consume=new Runnable() {
@Override
public void run() {
for(int i=0;i<10;i++){
con.take();
}
}
};
//创建线程对象
Thread zhuxi=new Thread(produce, "铸玺");
Thread bingbing=new Thread(consume, "冰冰");
Thread zhuxi2=new Thread(produce, "铸玺2");
Thread bingbing2=new Thread(consume, "冰冰2");
//启动线程
zhuxi.start();
zhuxi2.start();
bingbing.start();
bingbing2.start();
}
}
第三种:生产者和消费者(使用阻塞队列)
此方法的特点:不需要手动唤醒线程,当生产了一定数量后生产者队列就会自动进入等待队列中,等到消费者消费。消费线程也是一样。注意的是在运行的过程中可能会发生运行完生产者还没有及时打印,就别消费者抢走了导致控制台输出出现了问题,但是运行结果是正确的生产多少就消费多少。不会产生多消费或者多生产的情况。代码实现如下:
public class TestBlockingQueue {
public static void main(String[] args) {
//1创建阻塞队列
ArrayBlockingQueue<String> queue=new ArrayBlockingQueue<>(6);
// LinkedBlockingQueue queue=new LinkedBlockingQueue(6);
//2创建线程
Runnable produce=new Runnable() {
@Override
public void run() {
for(int i=0;i<30;i++) {
try {
queue.put(Thread.currentThread().getName() + "---" + i);
System.out.println(Thread.currentThread().getName()+"生产了:"+i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Runnable consume=new Runnable() {
@Override
public void run() {
for(int i=0;i<30;i++) {
try {
String take = (String)queue.take();
System.out.println(Thread.currentThread().getName()+"消费了:"+take);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
new Thread(produce,"铸玺").start();
new Thread(consume,"冰冰").start();
}
}