java并发-使用内置条件队列实现简单的有界缓存

原创 2014年12月30日 09:51:49

        内置锁和内置条件队列一起,一个简单的应用是创建可阻塞的有界缓存区,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(){
			@Override
			public void run() {
				try {
					bu.put("hello1");
				} catch (InterruptedException execption) {
					System.out.println("intercetp1:"+Thread.currentThread().getName());
				}
			}
		});
		Thread t2 = new Thread(new Runnable(){
			
			@Override
			public void run() {
				try {
					bu.put("hello2");
				} catch (InterruptedException execption) {
					System.out.println("intercetp2:"+Thread.currentThread().getName());
				}
			}
		});
		Thread t3 =  new Thread(new Runnable(){
			@Override
			public 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-2
Tue Dec 30 10:23:58 CST 2014 main take:hello1
Tue Dec 30 10:23:58 CST 2014 main take:hello2
Tue Dec 30 10:23:58 CST 2014 buffer is empty thread wait:main
Tue Dec 30 10:23:58 CST 2014 main take:hello3
Tue Dec 30 10:23:58 CST 2014 buffer is empty thread wait:main
Tue 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而被唤醒时,并不意味着它等待的条件已经为真了,这也是内置条件队列的局限所在。

版权声明:本文为博主原创文章,未经博主允许不得转载。

有界、无界队列对ThreadPoolExcutor执行的影响

Java提供了4钟线程池: newCachedThreadPool newFixedThreadPool newSingleThreadExecutor newScheduledThreadP...
  • kusedexingfu
  • kusedexingfu
  • 2017年05月18日 20:08
  • 5311

并行化资源池队列1——部分有界队列

并行化资源池队列 1前言      在并发系统中很多地方都要用到作为资源池的并行化队列,如在大多数应用中,一个或多个生产者线程生产数据,一个或多个消费者消费数据。这些数据元素可以是需要执行的的任务...
  • javacoffe
  • javacoffe
  • 2014年12月09日 19:19
  • 3032

java实现有界队列

package com.cyq;import java.util.LinkedList; import java.util.concurrent.TimeUnit; import java.util....
  • chinoukin
  • chinoukin
  • 2017年09月28日 17:21
  • 146

Java自带线程池和队列详细讲解

原文链接 http://blog.csdn.net/sd0902/article/details/8395677 Java线程池使用说明 一简介 线程的使用在java中占有极其重要的地位,在...
  • aaa5438438
  • aaa5438438
  • 2017年02月16日 20:35
  • 860

缓存队列的创建过程

缓存队列的创建过程
  • C_PlayBoy
  • C_PlayBoy
  • 2017年02月23日 08:51
  • 968

Java双缓冲队列实现

用Java实现了一个双缓冲队列,并给出了实例代码
  • haibo_hn
  • haibo_hn
  • 2017年04月18日 09:30
  • 1856

java中队列的使用

Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了Queue接口。Queue接口窄化了对LinkedList的方法的访问权限(即在方法中的参数类型...
  • kangbin825
  • kangbin825
  • 2017年01月01日 16:15
  • 3856

Java三个类实现内存缓存

有一个需求,本来打算用redis来做,但是发现redis的list不支持某一项超时设置, 所以就用java自己写了一个简单的缓存,操作类似redis,总共只有3个类: 项目地址:https://g...
  • u010402518
  • u010402518
  • 2016年08月06日 14:51
  • 3900

Java中缓存之内存缓存

Java中缓存之内存缓存 1.缓存为什么要存在      应用服务器资源是有限的,数据库每秒中接受请求的次数也是有限的。如果利用有限的资源来提供尽可能大的吞吐量呢,一个办法:减少计 算量,缩短请求...
  • lululove19870526
  • lululove19870526
  • 2016年04月08日 17:04
  • 6330

java 60 行代码写一个简单可用的并且带过期时间的内存缓存

60 行代码写一个简单可用的并且带过期时间的内存缓存 在一个小项目中需要特别简单的使用一个缓存。如果要引入 ehcache 等其他第三方缓存还需要增加 各种 jar 甚至需要增加配置文件。特别麻...
  • wab719591157
  • wab719591157
  • 2017年09月19日 14:41
  • 1341
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:java并发-使用内置条件队列实现简单的有界缓存
举报原因:
原因补充:

(最多只允许输入30个字)