java 可伸缩阻塞队列实现
最近一年多写的最虐心的代码。必须好好复习java并发了。搞了一晚上终于测试都跑通过了,特此纪念,以资鼓励!
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; /**
* 实现可调整大小的阻塞队列,支持数据迁移平衡reader,writer读取吸入速度,达到最大吞吐
* @author hanjie
*
*/
public class RecordBuffer { public static final Record CLOSE_RECORD = new Record() { @Override
public Object getColumnValue(String columnName) {
// TODO Auto-generated method stub
return null;
}
}; public static final Record SWITCH_QUEUE_RECORD = new Record() { @Override
public Object getColumnValue(String columnName) {
// TODO Auto-generated method stub
return null;
}
}; public Lock switchingQueueLock = new ReentrantLock();
public Condition readerSwitched = switchingQueueLock.newCondition();
public Condition writerSwitched = switchingQueueLock.newCondition();
public Condition switchFinished = switchingQueueLock.newCondition(); public volatile boolean readerSwitchSuccess = true;
public volatile boolean writerSwitchSuccess = true;
public volatile boolean switchingQueue = false;
public volatile boolean closed = false;
private volatile ArrayBlockingQueue<Record> queue;
private TaskCounter taskCounter; public RecordBuffer(TaskCounter taskCounter, int size) {
this.queue = new ArrayBlockingQueue<Record>(size);
this.taskCounter = taskCounter;
} public void resize(int newSize) {
try { if(closed){
return;
} switchingQueueLock.lock();
try {
//double check下,要不可能writer收到CLOSED_record已经 退出了。writerSwitched.await() 会hang住
if(closed){
return;
}
this.switchingQueue = true; ArrayBlockingQueue<Record> oldQueue = queue;
queue = new ArrayBlockingQueue<Record>(newSize);
this.readerSwitchSuccess = false;
this.writerSwitchSuccess = false; //先拯救下writer,可能writer刚好阻塞到take上,失败也没关系,说明老队列不空,writer不会阻塞到take
oldQueue.offer(SWITCH_QUEUE_RECORD); while (!writerSwitchSuccess) {
writerSwitched.await();
}
//writer先切换队列,然后reader可能阻塞在最后一个put上,清空下老队列拯救reader,让它顺利醒来
transferOldQueueRecordsToNewQueue(oldQueue); while (!readerSwitchSuccess) {
readerSwitched.await();
}
//前面的清空,刚好碰到reader要put最后一个,非阻塞式清空动作就有残留最后一个put
transferOldQueueRecordsToNewQueue(oldQueue); this.switchingQueue = false;
this.switchFinished.signalAll(); } finally {
switchingQueueLock.unlock();
} } catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
} private void transferOldQueueRecordsToNewQueue(ArrayBlockingQueue<Record> oldQueue)
throws InterruptedException {
List<Record> oldRecords = new ArrayList<Record>(oldQueue.size());
Record record = null;
while ((record = oldQueue.poll()) != null) {
oldRecords.add(record);
}
// 转移老队列剩下的记录到新队列
for (int i = 0; i < oldRecords.size(); i++) {
queue.put(oldRecords.get(i));
}
} public void close() {
this.closed = true;
switchingQueueLock.lock();
try {
//如果正在切换队列, 等切换做完才能,发送最后一个CLOSE
while (switchingQueue) {
switchFinished.await();
} this.queue.put(CLOSE_RECORD);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
finally{
switchingQueueLock.unlock();
}
} public void put(Record record) {
try { if (!queue.offer(record)) {
taskCounter.incrBufferFullCount();
if (!readerSwitchSuccess) {
notifyReaderSwitchSuccess();
}
queue.put(record);
}
taskCounter.incrReadCount();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
} private void notifyReaderSwitchSuccess() {
System.out.println("reader switch");
switchingQueueLock.lock();
try {
readerSwitchSuccess = true;
readerSwitched.signalAll();
} finally {
switchingQueueLock.unlock();
}
} public Record take() {
try { Record record = queue.poll();
//如果拿到了切换记录,则切换队列重试
if(record == SWITCH_QUEUE_RECORD){
if (!writerSwitchSuccess) {
notifyWriterSwitchSuccess();
}
record = queue.poll();
} if (record == null) {
taskCounter.incrBufferEmptyCount(); //调用take先检查是否正在切换,保证拿到新的队列
if (!writerSwitchSuccess) {
notifyWriterSwitchSuccess();
}
record = queue.take();
//如果很不幸刚好在take阻塞时候,切换,只能发送一个切换记录将其唤醒
if(record == SWITCH_QUEUE_RECORD){
if (!writerSwitchSuccess) {
notifyWriterSwitchSuccess();
}
record = queue.take();
}
}
if (record == CLOSE_RECORD) {
if (!writerSwitchSuccess) {
notifyWriterSwitchSuccess();
}
return null;
}
taskCounter.incrWriteCount();
return record;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
} private void notifyWriterSwitchSuccess() { System.out.println("writer switch");
switchingQueueLock.lock();
try {
writerSwitchSuccess = true;
writerSwitched.signalAll();
} finally {
switchingQueueLock.unlock();
} } }
java 可伸缩阻塞队列实现的更多相关文章
- Java多线程 阻塞队列和并发集合
转载:大关的博客 Java多线程 阻塞队列和并发集合 本章主要探讨在多线程程序中与集合相关的内容.在多线程程序中,如果使用普通集合往往会造成数据错误,甚至造成程序崩溃.Java为多线程专门提供了特有的 ...
- Java集合--阻塞队列及各种实现的解析
阻塞队列(Blocking Queue) 一.队列的定义 说的阻塞队列,就先了解下什么是队列,队列也是一种特殊的线性表结构,在线性表的基础上加了一条限制:那就是一端入队列,一端出队列,且需要遵循FIF ...
- Java:阻塞队列
Java:阻塞队列 本笔记是根据bilibili上 尚硅谷 的课程 Java大厂面试题第二季 而做的笔记 1. 概述 概念 队列 队列就可以想成是一个数组,从一头进入,一头出去,排队买饭 阻塞队列 B ...
- JAVA可阻塞队列-ArrayBlockingQueue
在前面的的文章,写了一个带有缓冲区的队列,是用JAVA的Lock下的Condition实现的,但是JAVA类中提供了这项功能,就是ArrayBlockingQueue, ArrayBlockingQu ...
- java 多线程阻塞队列 与 阻塞方法与和非阻塞方法
Queue是什么 队列,是一种数据结构.除了优先级队列和LIFO队列外,队列都是以FIFO(先进先出)的方式对各个元素进行排序的.无论使用哪种排序方式,队列的头都是调用remove()或poll()移 ...
- Java -- 使用阻塞队列(BlockingQueue)控制线程通信
BlockingQueeu接口是Queue的子接口,但是它的主要作用并不是作为容器,而是作为线程同步的工具. 特征: 当生产者线程试图向BlockingQueue中放入元素时,如果该队列已满,则该线程 ...
- Java并发--阻塞队列
在前面几篇文章中,我们讨论了同步容器(Hashtable.Vector),也讨论了并发容器(ConcurrentHashMap.CopyOnWriteArrayList),这些工具都为我们编写多线程程 ...
- Java中阻塞队列的使用
http://blog.csdn.net/qq_35101189/article/details/56008342 在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如 ...
- java并发阻塞队列
Java 并发编程利用 Condition 来实现阻塞队列 You are here: 开发&语言 - Java 文章 发布于 2017年06月26日 阅读 944 并发编程 什么是阻 ...
随机推荐
- 转【Oracle】一款非常好用的trace文件分析工具
[Oracle]一款非常好用的trace文件分析工具之一 北在南方 2016-04-14 11:23:58 浏览547 评论0 摘要: 介绍一款非常好用的10046分析工具--trca(Trace ...
- string截断
public static string GetFirstString(string stringToSub, int length) { Regex regex ...
- PHP CI框架如何去掉 sql 里的反引号
在使用CI框架的时候, 经常的Active Record 类,这时候会出现一个问题 使用Active Record 类组成的sql 中,为了防止sql注入,会自动的在表名,字段名 自动添加反引号 当然 ...
- linux svn安装(转载)
第一章 安装 1. 采用源文件编译安装.源文件共两个,为:subversion-1.6.1.tar.gz (subversion 源文件)subversion-deps-1.6.1.tar.gz (s ...
- VS2010安装顽疾解决方法:error 25541 failed to open xml file
一.问题描述 因为之前(2012)对HDFS客户端Thrift接口的库文件封装使用的是VS2010,最近考虑做一个完整的网盘系统的客户端,就把该库文件使用起来,比较悲剧的是之前做过操作系统的还原.我的 ...
- java操作Excel之POI(3)
一.字体处理 /** * 字体处理 */ public static void main(String[] args) throws Exception { Workbook wb = new HSS ...
- RDD之七:Spark容错机制
引入 一般来说,分布式数据集的容错性有两种方式:数据检查点和记录数据的更新. 面向大规模数据分析,数据检查点操作成本很高,需要通过数据中心的网络连接在机器之间复制庞大的数据集,而网络带宽往往比内存带宽 ...
- [UE4]事件处理(Handling Events)和委托(Delegate)代码示例(一)
1. 通过重写虚函数来处理事件 MyTriggerVolume.h 自定义一个Actor类,添加一个 Box 组件作为触发区域,然后通过重写虚函数——NotifyActorBeginOverlap, ...
- ExtJS的数据模型
给大家介绍一下ExtJS的组件模型. 常见的Ajax的开发流程: 1.定义URL,metod,params 2.开发后台 接收JSON/XML数据 返回JSON/XML数据 3.前台回调 4.显示到 ...
- 面对最菜TI战队,OpenAI在Dota2上输的毫无还手之力
作者:Tony Peng 去年,OpenAI 的 1v1 AI 击败了世界顶尖选手 Dendi,OpenAI CTO Greg Brockman 承诺:明年,我们会带着 5v5 的 AI bot 重回 ...