Java双缓冲队列实现

来源:互联网 发布:迪联软件 编辑:程序博客网 时间:2024/05/17 08:46

前言

        在某一模块中,需要将网络接收到的数据存入Oracle中。这是一个典型的生产者消费者场景,可以使用消息队列隔离生产者和消费者。由于接收的数据频度很高,而Oracle的插入速度较慢,为不影响接收端吞吐量,选择了双缓冲队列作为消息队列。双缓冲队列的原理是一般情况下生产者使用写队列,消费者使用读队列,两个线程不需要做同步保护。当读队列消费完的时候,将读写队列交换,生成者使用空的读队列,消费者使用写队列。双缓冲队列只需在这时对读写队列进行同步保护即可,可大幅提高性能。

        在使用Java开始编码时发现,Java的多线程同步和C++的差距有点大,怎么会没有条件变量、信号量、互斥锁?百度了几个实例后基本了解了Java的同步策略,比C/C++的要简单不少。不管是条件变量还是互斥锁,synchronized这一个关键字全都搞定。Java每个对象都自带锁,synchronized(obj) { // codeblock } 即可在此代码段保护obj对象;使用obj.wait()、obj.notify()以及obj.nofifyAll()又可以起到条件变量的作用,实在是太方便了。

实现

         代码主要由一个双缓冲队列类、一个生产者类和一个消费者类组成。需要说明的是,在我的这种实现里,只对写队列做了同步保护,这是因为在我的应用里读写队列交换由消费者控制,这样一来,读队列的使用以及交换全在消费者线程,读队列也就没必要同步了。如果改为定时交换的话,就必须对读队列也加保护了。

双缓冲队列类

         该类以单例模式实现,queue为单例对象。其中,Emp是我们在队列中要缓存的对象类,如果类型多的话可以将双缓冲队列类改造为模板类。成员变量包含两个列表,一个用来读,一个用来写。几个接口,push用来添加新消息,getWriteListSize获取写队列当前大小,getReadList获取读列表,swap用来交换读写列表。
public class DoubleBufferQueue {private List<Emp> readList = new ArrayList<Emp>();private List<Emp> writeList = new ArrayList<Emp>();private static DoubleBufferQueue queue = new DoubleBufferQueue();private DoubleBufferQueue() {}public static DoubleBufferQueue getInst() {return queue;}public void push(Emp value) {synchronized (writeList) {writeList.add(value);}}public int getWriteListSize() {synchronized (writeList) {return writeList.size();}}public List<Emp> getReadList() {return readList;}public void swap() {synchronized(writeList) {List<Emp> temp = readList;readList = writeList;writeList = temp;writeList.clear();}}}

生产者类

        生产者类是一个写线程,负责从服务器读取emp对象的一组实例,放入双缓冲队列的写队列里(代码里与应用相关的部分可以忽略,我临时修改的)。与双缓冲队列相关的调用都在线程函数run里。在调用push函数前,做了个简单的写入控制:比如当写队列已经缓存了一万个实例时,暂停写入,直到消费者线程用完读队列并交换读写队列。这种方式可能会导致网络吞吐量的降低,但如果能提高消费者效率就可以减少这种降低,比如采用批处理提高Oracle的插入速度。
public class Writer implements Runnable {private NetSession session = null;public Writer() {        session = new NetSession();}public boolean connect(String server, int port) {if (!session.connect(server, port)) {return false;}return true;}public void run() {DoubleBufferQueue queue = DoubleBufferQueue.getInst();while (!session.valid()) {try {MsgHead head = session.recvHead();Byte[] buffer = session.recvBody(head);if (head.type != EMP)continue;Emp emp = new Emp();emp.deserialize(buffer);while (queue.getWriteListSize() >= 10000) {try {Thread.sleep(200);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}queue.push(emp);} catch (NetIOException e) {}}}}

消费者类

         消费者类是一个读线程,负责将读队列的元素写入数据库(代码做了改动,写入数据库改为写入文件)。读线程不断检测读队列,当读队列有数据时,遍历读队列读取元素写入数据库。这里需要注意的是,在读线程里做了交换读写队列的控制,只有当读队列为空且写队列大小超过1000时才进行交换。这样做的好处一是可以避免交换频率过高,二是保证一次获取一定量的实例,可以使用数据库的批处理来提高写入效率。另外,在读队列使用完后,记得要清空读队列。
public class Reader implements Runnable {public void run() {// TODO Auto-generated method stubDoubleBufferQueue queue = DoubleBufferQueue.getInst();try {while (true) {List<Emp> readList = queue.getReadList();while (readList.isEmpty()) {try {if (queue.getWriteListSize() > 1000) {queue.swap();readList = queue.getReadList();} else {Thread.sleep(1);}} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}int counter = 0;FileWriter fw = new FileWriter("result.txt", true);   for (Emp value : readList) {counter++;fw.write(value.toString());fw.write("\n");}fw.close();System.out.println("Read: " + counter);readList.clear();}} catch (IOException e) {e.printStackTrace();}}}

应用

        应用部分较为简单,创建两个线程,开启运行即可。读写线程中的读写控制考虑的比较简单,有好想法的朋友欢迎交流,谢谢!
public static void rwTest() {Reader reader = new Reader();Thread t1 = new Thread(reader);Writer writer = new Writer();writer.connect("192.168.1.152", 8000);Thread t2 = new Thread(writer);t1.start();t2.start();}    


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 想家想的都哭怎么办 想学手艺被骗学费怎么办 小腿肚后面筋和肌肉压痛怎么办 尿道囗和屁股眼中间长疮怎么办? 2个多月的宝宝蛋蛋上有个包怎么办 幼儿园睡觉自己摸下身玩怎么办 猫猫打喷嚏有透明液体怎么办 夏季穿运动鞋出了脚气怎么办 脚底长了刺瘊子怎么办 凉鞋前面踢破了怎么办 尖头鞋前面折了怎么办 月子里宝宝蛋蛋破皮怎么办 军人在训练时想上侧所怎么办 被白蚂蚁咬了怎么办 被蚂蚁咬了起包怎么办 脚踢了石头肿了怎么办 脚大拇指踢肿了怎么办 被骨头咯到了疼怎么办 被开水烫着了疼怎么办 鞋子上踩了口香糖怎么办 鞋底踩到口香糖干了怎么办 鞋子不小心踩到口香糖怎么办 站久了膝盖痛怎么办 站久了脚底板痛怎么办 蛇疮好了以后痛怎么办 站久了脚趾痛怎么办 心脏被踢了一脚怎么办 从马背上摔下来怎么办 小孩蛋蛋碰撞后有积液怎么办 小孩蛋蛋大小不一样有积液怎么办 对派出所的笔录不服怎么办 蛋蛋让尿淹了发红有小红瘩达怎么办 手被皮筋弹肿了怎么办 手被皮筋勒肿了怎么办 皮筋把手挤肿了怎么办 猫被皮筋绑久肿了怎么办 抗链0高关节疼怎么办 近视800度老了怎么办 军检体重不达标怎么办 到交房租没有钱怎么办 房租没到期房东要收回怎么办