南京银城建设 网站,深圳网络推广哪家好,公司网址例子,建设网站需要买什么手续费#x1f468;#x1f393;作者简介#xff1a;一位大四、研0学生#xff0c;正在努力准备大四暑假的实习 #x1f30c;上期文章#xff1a;多线程JUC#xff1a;解决线程安全问题——synchronized同步代码块、Lock锁 #x1f4da;订阅专栏#xff1a;多线程作者简介一位大四、研0学生正在努力准备大四暑假的实习 上期文章多线程JUC解决线程安全问题——synchronized同步代码块、Lock锁 订阅专栏多线程JUC 希望文章对你们有所帮助 等待唤醒机制生产者消费者模式 等待唤醒机制等待唤醒机制的实现消费者代码实现生产者代码实现 阻塞队列实现等待唤醒机制 等待唤醒机制
等待唤醒机制也叫做生产者消费者模式打破了以前线程间执行的随机性生产者消费者模式能够使得线程之间是轮流运行的。是一个非常经典的多线程协作的模式。 对于两条线程其中一条为生产者另一条为消费者大家都是学习过操作系统的原理多少还是记得一些的。
对于等待唤醒机制其只有2种情况 1、消费者等待若没有可以被消费者消费的数据那么消费者就是进入wait状态这时候生产者就可以抢占CPU生产数据接着notify唤醒消费者 2、生产者等待若已经有数据供给消费者消费则生产者进入wait状态消费者抢占CPU消费数据接着notify唤醒生产者 在这其中可能会涉及到的方法
方法名称说明void wait()当前线程等待直到被其他线程唤醒void notify()随机唤醒单个线程void notifyAll()唤醒所有线程
等待唤醒机制的实现
消费者代码实现
消费者和生产者中间有一个控制他们执行相应操作的核心视为Controller记录一些状态变量和锁对象
public class Controller {/*** 控制消费者和生产者的执行*///表示是否有数据 0没有 1有public static int flag 0;//消费者最多可以消费的数据量public static int count 10;//锁对象public static Object lock new Object();
}接着实现消费者的逻辑
public class Consumer extends Thread{Overridepublic void run() {while(true){synchronized (Controller.lock) {if(Controller.count 0){//消费者已经消费量了10次退出break;}else{//先判断有无可以消费的数据if(Controller.flag 0) {//若无等待//用lock调用wait方法使得当前线程与锁进行绑定之后唤醒就唤醒这些被绑定了的线程try {Controller.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}else{//若有消费System.out.println(正在消费还可以消费 --Controller.count 个);//消费完后唤醒生产者唤醒绑定在这把锁上的所有线程Controller.lock.notifyAll();//修改控制中心的状态Controller.flag 0;}}}}}
}生产者代码实现
public class Producer extends Thread{Overridepublic void run() {while (true){synchronized (Controller.lock){if(Controller.count 0){break;}else{if(Controller.flag 1){//已经有供给消费者进行消费的数据try {Controller.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}else{System.out.println(成功生产);Controller.lock.notifyAll();Controller.flag 1;}}}}}
}最后编写测试类代码验证
public class ThreadDemo {public static void main(String[] args) {//创建线程对象Producer producer new Producer();Consumer consumer new Consumer();//给线程设置名字producer.setName(生产者);consumer.setName(消费者);//开启线程producer.start();consumer.start();}
}阻塞队列实现等待唤醒机制
何为阻塞队列其实就是连接生产者和消费者的一个队列管理着数据分别供消费者take和生产者的put如果put不进去或者take不出则说明队列满了或者空了这时候就会进入阻塞状态。
阻塞队列BlockingQueue本身实现了Iterable、Collection、Queue的接口无法直接实例化但是其具有2个实现类 1、ArrayBlockingQueue底层为数组有界 2、LinkedBlockingQueue底层为链表无界不是真正的无界最大为int的最大范围只是无须指定范围 利用阻塞队列来实现是很便捷的因为我们可以查看put和take方法的底层可以发现这两个方法是自带锁的所以我们在实现生产者和消费者的时候无须自己上锁否则反而会容易因为锁的嵌套而发生死锁。 生产者代码
public class Producer extends Thread{ArrayBlockingQueueString queue;public Producer(ArrayBlockingQueueString queue) {this.queue queue;}Overridepublic void run() {while (true) {//直接不断的把数据放进阻塞队列如果满了它自己会阻塞try {queue.put(数据);System.out.println(消费者生产了一个数据到阻塞队列);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}消费者代码
public class Consumer extends Thread{ArrayBlockingQueueString queue;public Consumer(ArrayBlockingQueueString queue) {this.queue queue;}Overridepublic void run() {while (true){try {String take queue.take();System.out.println(take);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}测试类
public class ThreadDemo {/*** 使用阻塞队列实现等待唤醒机制要保证生产者和消费者用的是同一个阻塞队列*/public static void main(String[] args) {//创建一个可以存放1个数据的阻塞队列ArrayBlockingQueueString queue new ArrayBlockingQueue(1);//创建生产者和消费者对象并把阻塞队列传递过去使得它们使用同一个阻塞队列Producer producer new Producer(queue);Consumer consumer new Consumer(queue);producer.setName(生产者);consumer.setName(消费者);producer.start();consumer.start();}
}最后显示可能会重复打印数据这是因为输出的语句没有放在锁里面锁可以执行的put和take已经写死了但是并不影响我们实际数据的并发安全性只是不方便我们的观察罢了。
至此阻塞队列实现等待唤醒机制的demo已经跑通了阻塞队列底层的执行实际上是异步的可以解决在实际生产环境中的超卖问题具体可以看我之前的文章 Redis原理速成项目实战——Redis实战9秒杀优化
当然主流的方法还是使用消息队列RabbitMQ或Kafka这个大家可以自行去了解。