在延迟队列中所保存的每一个元素内容.每当时间一到,(compareTo进行比较,getDelay()获取延迟时间),都会自动进行队里数据的弹出操作;
使用延迟队列(模拟讨论会依次离开的场景)
public class Student implements Delayed {
//姓名
private String name;
//离开时间
private long expire;
//停留时间
private long delay;
public Student(String name,long delay,TimeUnit unit){
this.name = name;
//转换时间单位为毫秒
this.delay = TimeUnit.MILLISECONDS.convert(delay,unit);
//失效时间计算
this.expire = System.currentTimeMillis() + this.delay;
}
public String toString() {
return this.name + "同学已经达到预计的停留时间"+TimeUnit.SECONDS.convert(this.delay,TimeUnit.MILLISECONDS)+"秒,已经离开了";
}
/**
* 队列弹出计算
* @param o
* @return
*/
@Override
public int compareTo(Delayed o) {
int i = (int) (this.delay - this.getDelay(TimeUnit.MILLISECONDS));
return i;
}
/**
*延迟时间计算
* @param
* @return
*/
@Override
public long getDelay(TimeUnit unit) {
long convert = unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
return convert;
}
}
public class JUCDemo {
public static void main(String[] args) throws InterruptedException {
//定义延迟队列
BlockingQueue<Student> queue = new DelayQueue<>();
//保存队列信息
queue.put(new Student("李",3, TimeUnit.SECONDS));
queue.put(new Student("王",5,TimeUnit.SECONDS));
//判断队列是否有数据
while (!queue.isEmpty()){
//获取弹出数据
Student take = queue.take();
System.out.println(take);
TimeUnit.SECONDS.sleep(1);
}
}
}
输出结果:
李同学已经达到预计的停留时间3秒,已经离开了
王同学已经达到预计的停留时间5秒,已经离开了
使用延迟队列的主要原因是它可以实现队列的定时清理操作.那么基于这样的自动清理机制就可以实现数据缓存的操作控制.这样的操作可以极大的提升项目的并发性能;
(在时间开发中,如果是基于数据库的查询操作,那么在多线并发量较高的情况下就有可能产生严重的性能问题,例如一个热门新闻可能会有上千万的访问量,这个时候采用直接数据库的读取模式就非常不理智.为了解决这一的问题,可以采用缓存模式,将一些重要的数据直接放到缓存里面.当不同的线程查询相同数据时先判断缓存中是否有指定内容,如果存在,则进行直接读取,如果不存在,再进行数据库加载.对于缓存中的内容还要考虑无效数据的清理问题,而有了延迟队列这种自动弹出的机制存在.这一操作实现就会变得非常容易)
下面实现一个新闻数据的缓存操作,考虑到可能会保存有多个数据,所以讲通过Map集合实现存储,同时考虑到缓存数据的修改安全性问题,使用ConcurrentHashMap子类,另外对于数据的弹出操作将通过守护线程进行处理.
public class News {
private long nid;
private String title;
public News(long nid, String title){
this.nid = nid;
this.title = title;
}
public String toString(){
return "新闻数据---->编号:"+this.nid+",标题:"+title;
}
}
public class Cache<K,V> {
//时间单位
private static final TimeUnit TIME = TimeUnit.SECONDS;
//缓存时间
private static final long DELAY_SECONDS = 2;
//缓存集合
private Map<K,V> cacheObjects = new ConcurrentHashMap<>();
private BlockingQueue<DelayedItem<Pair>> queue = new DelayQueue<>();
//启动线程
public Cache(){
Thread thread = new Thread(()->{
while (true){
try{
//数据弹出消费
DelayedItem<Pair> item = Cache.this.queue.take();
if (item != null){
//获取内容
Pair pair = item.getItem();
//删除数据
Cache.this.cacheObjects.remove(pair.key,pair.value);
}
}catch (Exception e){}
}
});
thread.setDaemon(true);
thread.start();
}
/**
* 保存数据
* @param key
* @param value
*/
public void put(K key,V value) throws InterruptedException {
V oldValue= this.cacheObjects.put(key,value);
//删除已有数据
if (oldValue != null){
this.queue.remove(oldValue);
}
//重新保存
this.queue.put(new DelayedItem<Pair>(new Pair(key,value),DELAY_SECONDS,TIME));
}
/**
* 获取缓存数据
* @param key
* @return
*/
public V get(K key){
return this.cacheObjects.get(key);
}
/**
* 封装保存数据
*/
private class Pair{
private K key;
private V value;
public Pair(K key,V value){
this.key = key;
this.value = value;
}
}
/**
* 延迟数据保存
* @param
*/
private class DelayedItem<T> implements Delayed{
private T item;
private long delay;
private long expire;
public DelayedItem(T item,long delay,TimeUnit unit){
this.item = item;
this.delay = TimeUnit.MILLISECONDS.convert(delay,unit);
this.expire = System.currentTimeMillis() + this.delay;
}
public T getItem(){
return this.item;
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.expire - System.currentTimeMillis(),TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
return (int)(this.delay - this.getDelay(TimeUnit.MILLISECONDS));
}
}
}
public class JUCDemo2 {
public static void main(String[] args) throws InterruptedException {
Cache<Long,News> cache = new Cache<>();
cache.put(1L,new News(1L,"程序员为什么秃"));
cache.put(2L,new News(2L,"程序员为什么宅"));
cache.put(3L,new News(3L,"程序员为什么扣"));
System.out.println(cache.get(1L));
System.out.println(cache.get(2L));
//模拟延迟时间.2秒后清除缓存
TimeUnit.SECONDS.sleep(3);
System.out.println(cache.get(3L));
}
}
输出结果:
新闻数据---->编号:1,标题:程序员为什么秃
新闻数据---->编号:2,标题:程序员为什么宅
null
在程序中考虑到缓存数据的自动清理问题,所以使用了延迟队列保存所以的数据信息(同时还有一份数据信息保存在Map集合中),为了保证延迟队列中的数据弹出后可以进行Map集合相应数据的删除.所以定义了一个守护线程接收延迟队列弹出的内容.由于本程序设置了默认的缓存时间为2秒,这样当2秒一过数据就会被删除.