java并发-使用内置条件队列实现简单的有界缓存
来源:互联网 发布:微店小票打印软件 编辑:程序博客网 时间:2024/05/17 03:25
内置锁和内置条件队列一起,一个简单的应用是创建可阻塞的有界缓存区,java并发包的BlockingQueue就是一个利用Lock和显式条件队列实现的可阻塞的有界队列。总结内置锁和内置条件的原理,这里我们用另一种方式实现简单的可阻塞缓存。源码如下:
首先,创建一抽象有界缓存类ABoundedBuffer,提供插入和删除的基本实现。
/** * @title :ABoundedBuffer * @description :有界缓存抽象类 * @update :2014-12-30 上午9:29:33 * @author :172.17.5.73 * @version :1.0.0 * @since :2014-12-30 */public abstract class ABoundedBuffer<V> {private final V[] buf;private int tail;private int head;private int count;protected ABoundedBuffer(int capacity){this.buf = (V[]) new Object[capacity];}protected synchronized final void doPut(V v){buf[tail] = v;if(++tail==buf.length){tail = 0;}++count;}protected synchronized final V doTake(){V v = buf[head];buf[head] = null;if(++head==buf.length){head = 0;}--count;return v;}public synchronized final boolean isFull(){return count == buf.length;}public synchronized final boolean isEmpty(){return count==0;}}
其次,利用内置条件队列,编写子类实现可阻塞的插入和删除操作。插入操作,依赖的条件是缓存非满,当条件不满足时,调用wait方法挂起线程,一旦插入成功,说明缓存非空,则调用notifyAll方法唤醒等待非空的线程。删除操作,依赖的条件是非空,当条件不满足时,同样挂起等待,一旦删除成功,说明缓存非满,唤起等待该条件的线程。简单的源码如下:
import java.util.Date;/** * * @title :InnerConditionQueue * @description :使用内置条件队列,实现简单的有界缓存 * 通过对象的wait和notify来实现挂起 * 锁对象是this,调用wait/notify的对象是同一个对象。 * 三元关系(锁、wait/notify、条件谓词) * 缺陷: * 线程从wait中被唤醒时,并不代码条件谓词为真,此时还是需要再判断条件。所以必须在循环中调用wait * 每次醒来时都判断谓词的真假。 * 谓词:对客体的描述或说明(是什么、怎么样、做什么),描述客体的本质、关系、特性等的词项。 * @update :2014-12-18 下午4:18:06 * @author :172.17.5.73 * @version :1.0.0 * @since :2014-12-18 */public class InnerConditionQueue<V> extends ABoundedBuffer<V> {protected InnerConditionQueue(int capacity) {super(capacity);}public synchronized void put(V v) throws InterruptedException{while(isFull()){System.out.println(new Date()+" buffer is Full thread wait:"+Thread.currentThread().getName());wait();}doPut(v);notifyAll();}public synchronized V take() throws InterruptedException{while(isEmpty()){System.out.println(new Date()+" buffer is empty thread wait:"+Thread.currentThread().getName());wait();}V v = doTake();//每当在等待一个条件时,一定要确保在条件谓词变为真时,通过某种方式发出通知notifyAll();System.out.println(new Date()+" "+Thread.currentThread().getName()+" take:"+v);return v;}}
最后,编写测试代码,创建一个大小为2的缓冲区,启动三个线程执行插入操作,当第三个线程执行时会因为缓存已满而挂起,主线程删除两个记录后,等待线程被唤醒成功插入。当缓存空的时候,之后的删除操作将被阻塞直到有新的记录插入为止。测试代码如下:
import java.util.Date;public class Main {public static void main(String[] args) {final InnerConditionQueue<String> bu = new InnerConditionQueue<String>(2);Thread t1 = new Thread(new Runnable(){@Overridepublic void run() {try {bu.put("hello1");} catch (InterruptedException execption) {System.out.println("intercetp1:"+Thread.currentThread().getName());}}});Thread t2 = new Thread(new Runnable(){@Overridepublic void run() {try {bu.put("hello2");} catch (InterruptedException execption) {System.out.println("intercetp2:"+Thread.currentThread().getName());}}});Thread t3 = new Thread(new Runnable(){@Overridepublic void run() {try {bu.put("hello3");Thread.sleep(50000);bu.put("last one...");} catch (InterruptedException execption) {System.out.println("intercetp3:"+Thread.currentThread().getName());}}});t1.start();t2.start();t3.start();try {Thread.sleep(5000);bu.take();bu.take();bu.take();bu.take();} catch (InterruptedException execption) {execption.printStackTrace();}System.out.println(new Date()+" main over...");}}
执行结果:t3的第一个put操作会因为缓存已满而阻塞,5秒后主线程删除两个操作后,重新被唤醒。主线程的第四个bu.take()操作会因为缓存为空而阻塞,直到t3在50秒后重新插入"last one"后被唤醒,操作结束。
Tue Dec 30 10:23:53 CST 2014 buffer is Full thread wait:Thread-2Tue Dec 30 10:23:58 CST 2014 main take:hello1Tue Dec 30 10:23:58 CST 2014 main take:hello2Tue Dec 30 10:23:58 CST 2014 buffer is empty thread wait:mainTue Dec 30 10:23:58 CST 2014 main take:hello3Tue Dec 30 10:23:58 CST 2014 buffer is empty thread wait:mainTue Dec 30 10:24:48 CST 2014 main take:last one...Tue Dec 30 10:24:48 CST 2014 main over...
结论:BlockingQueue的子类ArrayBlockingQueue是使用ReentrantLock和ObjectCondition实现的可阻塞队列,该实现选取显式锁和显式条件队列的原因是很显然,这个队列的入队和出队操作依赖两个条件,而ReentrantLock可以关联多个条件队列。它与我们用Object的内置队列相比,巧妙之处在于:线程会在其等待的条件队列中等待。我们的例子中非空和非满这两种条件都关联着同一个条件队列,当一个线程由于其他线程调用了notifyAll而被唤醒时,并不意味着它等待的条件已经为真了,这也是内置条件队列的局限所在。
- java并发-使用内置条件队列实现简单的有界缓存
- JAVA并发-条件队列
- 使用Java实现简单的队列(queue)
- java并发编程条件队列的唤醒机制探究
- 使用JUC并发工具包的Lock和Condition,实现生产者和消费者问题中的有界缓存
- java实现有界队列
- java缓存的简单实现
- 简单的java缓存实现
- 简单的java缓存实现
- Java简单队列的实现
- 【java并发】阻塞队列的使用
- 实现有大小限制的并发阻塞队列
- 如何使用Java实现简单的本地缓存?
- java并发等待条件的实现原理(Condition)
- java并发编程学习: 阻塞队列 使用 及 实现原理
- (C#)使用队列(Queue)解决简单的并发问题
- 使用队列(Queue)解决简单的并发问题
- 聊聊高并发(十四)理解Java中的管程,条件队列,Condition以及实现一个阻塞队列
- FreeRTOS 在STM32上的移植 V1.0
- 黑马程序员—Java入门学习日记基础篇-面向对象总结4
- Java线程:线程的交互
- 利用monkey测试android,入门级用户可能遇见的错误及解决办法
- B-LINK USB无线网卡 ubuntu 14.04 驱动解决
- java并发-使用内置条件队列实现简单的有界缓存
- f
- leetcode 172: Factorial Trailing Zeroes
- linux下编译c或c++
- java之简易生成彩色二维码实践
- ios: 项目icon和default图片命名规则
- crontab格式
- Asp.Net MVC 4使用RouteDebugger调试Route路径
- 真机调试iphone应用程序