目录

  • 手撕BlockingQueue
  • 生产者-消费者模式
  • P问题、NP问题

手撕BlockingQueue

public class RequestQueue {private final Queue<Request> queue = new LinkedList<>();private Lock lock = new ReentrantLock();private Condition condition = lock.newCondition();public  Request getRequest() {try {lock.lock();while (queue.peek() == null) {try {condition.await();} catch (InterruptedException e) {e.printStackTrace();}}return queue.remove();} finally {lock.unlock();}}public void putRequest(Request request) {try {lock.lock();queue.offer(request);condition.signalAll();} finally {lock.unlock();}}
}

生产者-消费者模式

在实现生产者消费者问题时,可以采用三种方式:
1.使用Object的wait/notify的消息通知机制
2.使用Lock的Condition的await/signal的消息通知机制
3.使用BlockingQueue实现

一.wait/notify实现生产者和消费者模式

public class ProductAndConsumer{/*1.生产者不断的向里面加东西2.消费者不断地取东西3.生产者发现容器里满了就wait4.消费者发现容器里没东西了就wait5.生产者生产东西唤醒消费者6.消费者消费东西唤醒生产者7.锁对象要是同一个对象才行*/public static void main(String[] args){ProductAndConsumer ps = new ProductAndConsumer();Resources resources = ps.new Resources(10,new LinkedList<Product>());new Thread(ps.new Producer(resources)).start();new Thread(ps.new Consumer(resources)).start();}class Product{String name;public Product(String name) {this.name = name;}public String getName() {return name;}}//生产者class Producer implements Runnable{Resources resources;@Overridepublic void run() {while (true){String name = (System.currentTimeMillis()+">"+(int)(Math.random()*100))+"号产品";System.out.println("生产者生产了:"+name);resources.put(new Product(name));}}public Producer(Resources resources) {this.resources = resources;}}//消费者class Consumer implements Runnable{Resources resources;@Overridepublic void run() {while (true){Product product = resources.pop();System.out.println("消费者消费了:"+product.name);}}public Consumer(Resources resources) {this.resources = resources;}}class Resources{int maxLength;private List<Product> productList;synchronized void put(Product product){while (productList.size()==maxLength){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}productList.add(product);this.notifyAll();}synchronized Product pop(){while(productList.isEmpty()){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}Product product = productList.remove(0);this.notifyAll();return product;}public Resources(int maxLength, List<Product> productList) {this.maxLength = maxLength;this.productList = productList;}}
}

二.使用Lock的Condition的await和signal的方式
  Lock用于控制多线程对同一状态的顺序访问,保证该状态的连续性。Condition用于控制多线程之间的、基于该状态的条件等待。

重点:
  Condition它更强大的地方在于:能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。

public class ProductAndConsumerLock {class Resources{int maxLength;private List<Product> productList;ReentrantLock lock;Condition full;Condition empty;void put(Product product){lock.lock();while (productList.size()==maxLength){try {full.await();} catch (InterruptedException e) {e.printStackTrace();}}productList.add(product);empty.signalAll();lock.unlock();}Product pop(){lock.lock();while(productList.isEmpty()){try {empty.await();} catch (InterruptedException e) {e.printStackTrace();}}Product product = productList.remove(0);full.signalAll();lock.unlock();return product;}public Resources(int maxLength, List<Product> productList, ReentrantLock lock, Condition full, Condition empty) {this.maxLength = maxLength;this.productList = productList;this.lock = lock;this.full = full;this.empty = empty;}}public static void main(String[] args) {ProductAndConsumerLock ps = new ProductAndConsumerLock();ReentrantLock lock = new ReentrantLock();Condition full = lock.newCondition();Condition empty = lock.newCondition();Resources resources = ps.new Resources(10, new LinkedList<Product>(), lock, full,empty);new Thread(ps.new Producer(resources)).start();new Thread(ps.new Consumer(resources)).start();}
}

三.使用BlockingQuene实现
  BlockingQueue即阻塞队列,它是基于ReentrantLock,依据它的基本原理,我们可以实现生产者与消费者模式,大致如下图所示:

  在Java中,BlockingQueue是一个接口,它的实现类有ArrayBlockingQueue、DelayQueue、 LinkedBlockingDeque、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue等,它们的区别主要体现在存储结构上或对元素操作上的不同,但是对于take与put操作的原理,却是类似的。

BlockingQueue的入队和出队的方法有很多,下面是各种方法的区别
1.入队
offer(E e):如果队列没满,立即返回true; 如果队列满了,立即返回false–>不阻塞
put(E e):如果队列满了,一直阻塞,直到队列不满了或者线程被中断–>阻塞
offer(E e, long timeout, TimeUnit unit):在队尾插入一个元素,,如果队列已满,则进入等待。

2.出队
poll():如果没有元素,直接返回null;如果有元素,出队
take():如果队列空了,一直阻塞,直到队列不为空或者线程被中断–>阻塞
poll(long timeout, TimeUnit unit):如果队列不空,出队;如果队列已空且已经超时,返回null;如果队列已空且时间未超时,则进入等待。

在这里,我们使用put和take的方式实现,生产者消费者模式
生产者类:

import java.util.concurrent.BlockingQueue;public class Producer implements Runnable {BlockingQueue<String> queue;public Producer(BlockingQueue<String> queue){this.queue=queue;}@Overridepublic void run() {try {String temp="消费线程"+Thread.currentThread().getName();System.out.println("生产线程"+Thread.currentThread().getName());queue.put(temp);} catch (InterruptedException e) {e.printStackTrace();}}
}

消费者类:

import java.util.concurrent.BlockingQueue;public class Customer implements Runnable {BlockingQueue<String> queue;public Customer(BlockingQueue<String> queue){this.queue=queue;}@Overridepublic void run() {try {String temp=queue.take();System.out.println(temp);} catch (InterruptedException e) {e.printStackTrace();}}
}

测试类:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;public class Test {public static void main(String[] args) {BlockingQueue<String> queue=new LinkedBlockingQueue<String>(2);Producer producer=new Producer(queue);Customer customer=new Customer(queue);for(int i=0;i<5;i++){new Thread(producer,"Producer"+(i+1)).start();new Thread(customer,"Customer"+(i+1)).start();}}
}

P问题、NP问题

研究P/NP问题的原因:
  因为计算机处理的输入常常不是那么几十个几千个那么一点点,想象一下,当计算机处理的数据达到100万个的时候,时间复杂度为o(n^ 2)和o(e^ n)的算法,所需的运行次数简直是天壤之别,o(e^n)指数级的可能运行好几天都没法完成任务,所以我们才要研究一个问题是否存在多项式时间算法。而我们也只在乎一个问题是否存在多项式算法,因为一个时间复杂度比多项式算法还要复杂的算法研究起来是没有任何实际意义的。

P类问题:存在多项式时间算法的问题。(P:polynominal,多项式)
NP类问题:能在多项式时间内验证得出一个正确解的问题。(NP:Nondeterministic polynominal,非确定性多项式)

P类问题是NP问题的子集,因为存在多项式时间解法的问题,总能在多项式时间内验证它。NP类问题,不知道这个问题是不是存在多项式时间内的算法,所以叫non-deterministic非确定性,但是可以在多项式时间内验证并得出这个问题的一个正确解。举个例子,

  著名的NP类问题:旅行家推销问题(TSP)。即有一个推销员,要到n个城市推销商品,他要找出一个包含所有n个城市的环路,这个环路路径小于a。我们知道这个问题如果单纯的用枚举法来列举的话会有(n-1)! 种,已经不是多项式时间的算法了,(注:阶乘算法比多项式的复杂)。那怎么办呢?我们可以用猜的,假设我人品好,猜几次就猜中了一条小于长度a的路径,我画画画画,好的,我得到了一条路径小于a的环路,问题解决了,皆大欢喜。可是,我不可能每次都猜的那么准,也许我要猜完所有种呢?所以我们说,这是一个NP类问题。也就是,我们能在多项式的时间内验证并得出问题的正确解,可是我们却不知道该问题是否存在一个多项式时间的算法,每次都能解决他(注意,这里是不知道,不是不存在)。

  因为计算机处理的输入常常不是那么几十个几千个那么一点点,想象一下,当计算机处理的数据达到100万个的时候,时间复杂度为o(n^ 2)和o(e^ n)的算法,所需的运行次数简直是天壤之别,o(e^n)指数级的可能运行好几天都没法完成任务,所以我们才要研究一个问题是否存在多项式时间算法。而我们也只在乎一个问题是否存在多项式算法,因为一个时间复杂度比多项式算法还要复杂的算法研究起来是没有任何实际意义的。所以这就引出了这类讨论的一个千年问题:是否 NP类问题=P类问题?即,是否所有能在多项式时间内验证得出正确解的问题,都是具有多项式时间算法的问题呢?要是解决了这个问题,那岂不是所有的NP问题都可以通过计算机来解决?有的存在,有的不存在。

  因为计算机处理的输入常常不是那么几十个几千个那么一点点,想象一下,当计算机处理的数据达到100万个的时候,时间复杂度为o(n^ 2)和o(e^ n)的算法,所需的运行次数简直是天壤之别,o(e^n)指数级的可能运行好几天都没法完成任务,所以我们才要研究一个问题是否存在多项式时间算法。而我们也只在乎一个问题是否存在多项式算法,因为一个时间复杂度比多项式算法还要复杂的算法研究起来是没有任何实际意义的。为了证明这个千古难题,科学家想出了很多办法。其中之一就是问题的约化。所谓问题约化就是,可以用问题B的算法来解决A ,我们就说问题A可以约化成问题B。举个例子,一元一次方程的求解,跟二元一次方程的求解,我们知道,只要能求解二元一次方程,那就可以用二元一次方程的解法来求解一元一次方程,只需要将一元一次方程加上y,并附加一个方程y=0就可以将一元一次方程变形为一个二元一次方程,然后用二元一次方程的解法来求解这个方程。注意,这里二元一次方程的解法会比一元一次的复杂。所以我们说,只需要找到解二元一次方程的规则性解法,那就能用这个规则性解法来求解一元一次方程。从这里也可以看出,约化是具有传递性的,如A约化到B,B约化到C,A就可以约化到C,同时不断约化下去,我们会发现一个很惊人的特性,就是他一定会存在一个最大的问题,而我们只需要解决了这个问题,那其下的所有问题也就解决啦,即NPC问题的概念。
  引到NP问题里就是,对于同一类的所有的NP类问题,若他们都可以在多项式时间内约化成最难的一个NP类问题,(我们直观的认为,被约化成的问题应具有比前一个问题更复杂的时间复杂度)当我们针对这个时间复杂度最高的超级NP问题要是能找到他的多项式时间算法的话,那就等于变向的证明了其下的所有问题都是存在多项式算法的,即NP=P。给出NPC问题定义。

NPC类问题(Nondeterminism Polynomial complete)
  存在这样一个NP问题,所有的NP问题都可以约化成它。换句话说,只要解决了这个问题,那么所有的NP问题都解决了。其定义要满足2个条件: 首先,它得是一个NP问题;然后,所有的NP问题都可以约化到它。要证明npc问题的思路就是: 先证明它至少是一个NP问题,再证明其中一个已知的NPC问题能约化到它。

NP难问题(NP-hard问题)
  NP-Hard问题是这样一种问题,它满足NPC问题定义的第二条但不一定要满足第一条(就是说,NP-Hard问题要比 NPC问题的范围广,NP-Hard问题没有限定属于NP),即所有的NP问题都能约化到它,但是他不一定是一个NP问题。
   NP-Hard问题同样难以找到多项式的算法,但它不列入我们的研究范围,因为它不一定是NP问题。即使NPC问题发现了多项式级的算法,NP-Hard问题有可能仍然无法得到多项式级的算法。事实上,由于NP-Hard放宽了限定条件,它将有可能比所有的NPC问题的时间复杂度更高从而更难以解决。

以上四个问题它们之间的关系可以用下图来表示:

手撕生产者-消费者模式 | P问题、NP问题相关推荐

  1. 面试官让我手写一个生产者消费者模式?

    不知道你是否遇到过面试官让你手写生产者消费者代码.别说,前段时间有小伙伴还真的遇到了这种情况,当时是一脸懵逼. 但是,俗话说,从哪里跌倒就要从哪里爬起来.既然这次被问到了,那就回去好好研究一下,争取下 ...

  2. 生产者/消费者模式之深入理解

    ★简介 生产者消费者模式并不是GOF提出的23种设计模式之一,23种设计模式都是建立在面向对象的基础之上的,但其实面向过程的编程中也有很多高效的编程模式,生产者消费者模式便是其中之一,它是我们编程过程 ...

  3. 生产者/消费者模式的理解及实现

    ★简介 生产者消费者模式并不是GOF提出的23种设计模式之一,23种设计模式都是建立在面向对象的基础之上的,但其实面向过程的编程中也有很多高效的编程模式,生产者消费者模式便是其中之一,它是我们编程过程 ...

  4. 生产者消费者模式详解(转载)

    ★简介 在实际的软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类.函数.线程.进程等).产生数据的模块,就形象地称为生产者:而处理 ...

  5. 设计模式之生产者消费者模式

    简介 通过前几篇文章的学习,我想你已经彻底掌握了wait()方法和notify()方法如何使用以及在哪种情况下使用了,本片文章我们将讲解下设计模式中的生产者消费者模式,我将通过手写一份生产者消费者模式 ...

  6. 深入浅出生产者-消费者模式

    笔者也建立的自己的公众号啦,平时会分享一些编程知识,欢迎各位大佬支持~ 扫码或微信搜索北风IT之路关注 生产者-消费者模式是一个经典的多线程设计模式,它为多线程间的协作提供了良好的解决方案.也经常有面 ...

  7. 【C++】多线程(链式、循环队列)实现生产者消费者模式

    生产者消费者模式:         生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同 ...

  8. java lock condition_Java 通过 Lock 和 竞争条件 Condition 实现生产者消费者模式

    更多 Java 并发编程方面的文章,请参见文集<Java 并发编程> 竞争条件 多个线程共享对某些变量的访问,其最后结果取决于哪个线程偶然在竞争中获胜. condition.await() ...

  9. 生产者/消费者模式(阻塞队列)

    生产消费者模式  貌似也是阻塞的问题  花了一些时间终于弄明白这个鸟东东,以前还以为是不复杂的一个东西的,以前一直以为和观察者模式差不多(其实也是差不多的,呵呵),生产消费者模式应该是可以通过观察者模 ...

最新文章

  1. 请填写红包接口调用ip_SOME/IP的车载网络应用
  2. jax_ws_对状态代码使用JAX-RS异常
  3. Java在电脑桌面的图标_电脑桌面图标都没了,教您电脑桌面图标都没了怎么恢复...
  4. 第七章 控制PL/SQL错误
  5. android调用so封装jni,GitHub - Michelle0716/AndroidJniDemo1: 安卓对c进行so文件打包,并以jni的形式进行调用...
  6. java mod函数的使用方法_java 数学计算的具体使用
  7. 深入理解Magento – 第六章 – 高级Magento模型
  8. scrollLeft. float
  9. 猎豹MFC--CFile类家族介绍ADO连接数据库 打开数据库 关闭数据库 连接字符串
  10. Bailian3659 判断是否为C语言的合法标识符【文本处理】
  11. 快速地将SolidWorks模型导入Adams
  12. Linux for嵌套if的格式,Shell篇(2)-条件语句if及循环语句 for , while , unti
  13. linux lilo命令,lilo命令_Linux lilo 命令用法详解:安装核心载入开机管理程序
  14. 打印符号三角形问题java_回溯法之符号三角形问题
  15. UI设计中图标设计规范是什么
  16. 机器人导航答辩记录半成品-60分模板-
  17. 千行代码率和代码行数总量
  18. snmptrap 命令解析
  19. P1502 窗口的星星题解(扫描线)
  20. 高等工程数学 —— 第三章(2)奇异值分解和A的加号逆

热门文章

  1. 支付宝php sdk如何使用,支付宝SDK怎么用啊?
  2. 范式(Paradigm)是什么?
  3. 我们需要一个什么样的公司---读《重新定义公司》有感
  4. spring-cloud-stream-binder-kafka发消息指定 partitionKey
  5. 文本搜索引擎Lucene之filed详解和代码测试
  6. 如何用OpenCV改变图片的大小?
  7. APICloud常用
  8. Android RecyclerView优雅实现复杂列表布局(二)
  9. Leco题目:两数之和
  10. M1 和 Docker 谈了个恋爱