线程通讯之生产者消费者
- 线程 操作(方法) 资源类
- 判断 执行 通知
- 防止虚假唤醒(await()的判断逻辑必须放到一个循环while里面,不能放在if里面)
实例:一个初始值为0的变量,两个线程对其进行交替操作,一个加1一个减1(生产一个,消费一个),来5轮
实现方式
传统版 1): Synchronized锁、await()等待、notify()唤醒
传统版 2): Lock锁(Condition的使用)、await()等待、signalAll()唤醒
传统版 2)的代码示例和解释:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 线程通讯之生产者消费者模式 传统版 实例:一个初始值为0的变量,两个线程对其进行交替操作,一个加1一个减1(生产一个,消费一个),来5轮
* 线程 操作(方法) 资源类
* 判断 执行 通知
* 防止虚假唤醒(await()的判断逻辑必须放到一个循环while里面,不能放在if里面)
*/
public class ProConsumer_TroditionDemo {
public static void main(String[] args) {
ShareData shareData = new ShareData();
//线程AAA
new Thread(()->{
for(int i=1;i<=5;i++){
//执行加1(生产操作)
shareData.increment();
}
},"AAA").start();
//线程BBB
new Thread(()->{
for(int i=1;i<=5;i++){
//执行减1(消费操作)
shareData.decrement();
}
},"BBB").start();
}
}
// 资源类
class ShareData {
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
// 加1方法
public void increment() {
lock.lock();
try {
// 1)判断,number!=0为false,默认不会进入while循环
while (number != 0) {
// 等待,不能生产
condition.await();// 新版写法,线程等待
}
// 2)干活
number++;
System.out.println(Thread.currentThread().getName() + "\t" + number);
// 3)通知唤醒线程
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
// 减1方法
public void decrement() {
lock.lock();
try {
// 1)判断,number!=0为false,默认不会进入while循环
while (number == 0) {
// 等待,不能生产
condition.await();// 新版写法,线程等待
}
// 2)干活
number--;
System.out.println(Thread.currentThread().getName() + "\t" + number);
// 3)通知唤醒线程
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
打印结果:
阻塞队列版的代码示例和解释:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 线程通讯之生产者消费者模式
* 阻塞队列版(消息中间件基本上就是这个原理)
*
*/
//资源类
class MySource{
//是否开启生产和消费的标志,true默认开启,volatile保证可见性
private volatile boolean FLAG = true;
//原子引用,保证原子性
private AtomicInteger atomicInteger = new AtomicInteger();
//通顺、适配和通用,传接口不传具体类
//注入方式两种:set/get设值注入,构造注入
BlockingQueue<String> blockingQueue = null;
public MySource(BlockingQueue<String> blockingQueue){
this.blockingQueue = blockingQueue;
System.out.println(blockingQueue.getClass().getName());//方便日志排查
}
//生产操作
public void myProd() throws Exception{
String data = null;
boolean returnValue;
while(FLAG){
data = atomicInteger.incrementAndGet()+"";
returnValue = blockingQueue.offer(data,2L,TimeUnit.SECONDS);
if(returnValue){
System.out.println(Thread.currentThread().getName()+"\t 插入队列"+data+"成功");
}else{
System.out.println(Thread.currentThread().getName()+"\t 插入队列"+data+"失败!");
}
TimeUnit.SECONDS.sleep(1);//1秒生产一个
}
System.out.println("FLAG==false,此时表示停止生产");
}
//消费操作
public void myConsumer() throws Exception{
String result = null;
while(FLAG){
result = blockingQueue.poll(2L,TimeUnit.SECONDS);//超过2秒没有取到,就停止取操作,返回null
if(result == null || result.equalsIgnoreCase("")){
FLAG = false;
System.out.println(Thread.currentThread().getName()+"\t 超过两秒没有取到,消费退出");
System.out.println();
System.out.println();
return;
}
System.out.println(Thread.currentThread().getName()+"\t 消费队列"+result+"成功");
}
}
//停止方法
public void stop() throws Exception{
this.FLAG = false;
}
}
public class ProConsumer_TroditionDemo {
public static void main(String[] args) {
MySource mysource = new MySource(new ArrayBlockingQueue<>(10));
// 生产者线程
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t 生产者线程启动");
try {
mysource.myProd();
} catch (Exception e) {
e.printStackTrace();
}
},"Prod").start();
// 消费者线程
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"\t 消费者线程启动");
System.out.println();
try {
mysource.myConsumer();
} catch (Exception e) {
e.printStackTrace();
}
}, "Consumer").start();
try {
Thread.currentThread().sleep(5000);//暂停5秒住线程
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println();
System.out.println();
System.out.println();
System.out.println("5秒钟时间到,停止操作");
try {
mysource.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
}
打印结果: