免费网站收录,福州网站建设哪家好,不允许访问网站,大连城乡住房建设厅网站文章目录 #x1f490;生产者消费者模型#x1f490;模拟实现阻塞队列#x1f4a1;注意点一#x1f4a1;注意点二 阻塞队列是一种“特殊”的数据结构#xff0c;但是也遵循队列的“先进先出”特性#xff0c;它的特殊在于#xff1a;
阻塞队列的两个特性#xff1a; 1… 文章目录 生产者消费者模型模拟实现阻塞队列注意点一注意点二 阻塞队列是一种“特殊”的数据结构但是也遵循队列的“先进先出”特性它的特殊在于
阻塞队列的两个特性 1、阻塞队列是线程安全的 2、带有阻塞特性 a.向队列中添加元素时如果队列满了就会阻塞等待直到其他线程从队列中取走元素时才会解除等待 b.从队列中向外拿元素时如果队列为空就会阻塞等待直到其他线程向队列中添加元素后才会解除等待 阻塞队列的最大作用就是可以用来实现“生产者消费者模型”
生产者消费者模型
什么是生产者消费者模型 比如在过年时包饺子妈妈负责擀饺子皮我负责包饺子妈妈会把擀好的饺子皮放在板子上此时我就可以拿饺子皮开始包饺子当板子上没有饺子皮时我就要等妈妈擀如果当板子上的饺子皮满了的时候妈妈就会停止擀饺子皮等到板子上有位置了以后才会继续擀所以妈妈就相当于是一个生产者我就相当于是一个消费者放饺子皮的板子就相当于是一个阻塞队列。 1、生产者消费者模型可以解决强耦合问题 什么是强耦合 例如有一个简单的分布式系统客户端向服务器机房发送一个要求后等待服务器响应但是这个要求可能需要两个服务器联合进行处理假设有一个A服务器和B服务器A服务器可能需要B服务器的响应后再进行后序的处理但是呢如果因为B服务器挂了那么就会导致A服务器也会挂所以这种联系较紧密的强耦合模块就很容易出现问题 针对上述情况就可以引入一个阻塞队列来解决 将阻塞队列也封装成一个单独的服务器程序让A服务器直接和这个阻塞队列进行交互让B服务器也直接和阻塞队列进行交互不管B到底会不会出错也都不会影响到AA都只会和阻塞队列进行交互就算以后再增加多个服务器的话也就只是和阻塞队列进行交互A也不必进行修改这样就降低了耦合性 2、削峰填谷 ”峰“的意思是短时间内请求量比较多 ”谷”的意思是:请求量比较少 如果在没有引入阻塞队列的情况下(如上图一)如果客户端这边的请求量比较大那么不管A接受多少请求量都会直接发给BA和B所要承受的压力是一样的但是每个服务器都是不一样的可能就会出现对于这个并发量A服务器可以承受B服务器就承受不了 对于这种情况就可以用生产者消费者模型解决 和图二一样给A发过去的请求量并不直接发给B而是发队列B仍然按照他自己的速度处理请求举个例子假如A和B每秒钟可以处理1000条请求但是出现了极端情况A每秒处理了3000条请求但是呢B仍然会继续按照每秒处理1000条请求这样请求就会在队列中积压如果队列满了的话A向队列中发送请求就发生阻塞直到B从队列中取出请求使队列出现空余空间后A再继续发出请求就这样一来一回虽然可能响应有点慢了但是总比把B服务器搞挂了好而后续A服务器发出的请求量少了以后B服务器就可以慢慢的处理完这些积压的请求 有了这样的机制后就可以保证在突发情况突然来临时服务器仍然能够正常运行 模拟实现阻塞队列
class MyBlockingQueueT {private Object[] element new Object[5];//为了避免内存可见性问题加上一个volatileprivate volatile int head;//记录当前要弹出元素的位置private volatile int tail;//记录当前要添加元素的位置private volatile int size;//向队列中添加一个元素public void put(T elem) {//加锁synchronized(this) {//判断队列是否满了while(size element.length) {//阻塞等待try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}//没满情况element[tail] elem;tail;//判断tail是否是数组最后一个元素if(tail element.length){tail 0;}size;this.notify();}}//弹出队列中的一个元素public T take() {synchronized (this) {//判断队列是否为空while(size 0) {//阻塞等待try {this.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}//不为空情况T ret (T)element[head];head;if(head element.length) {head 0;}size--;this.notify();return ret;}}}注意点一 put() 方法里面的wait需要take()里面的notify唤醒take()方法里面的wait需要put() 里面的notify唤醒因为当在put里面调用wait后就会处于阻塞等待就算是继续调用put仍然会阻塞等待必须调用take里面的notify后才后解除等待take也同理所以不可能出现方法内调用notify解除方法自己的wait这中情况
注意点二
在这里为什么要使用while循环判断 wait被唤醒的方式除了notify外还有一种方式就是如果在其他线程中调用interupt方法可能会中断wait的等待就如上面代码一样如果一旦调用了interupt方法那么wait就会被唤醒被唤醒之后代码并没有退出这时候如果数组仍然是满的话那么就会出现bug此时的tail指向的还是一个有效元素这个有效元素就会被修改并且sizesize就超过了数组的长度所以利用while语句一旦wait被唤醒那么就再次进行一下判断如果数组是满的仍然继续等待反之向下执行take也同理