博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
线程安全的无锁RingBuffer的实现【多个写线程一个读线程】
阅读量:2435 次
发布时间:2019-05-10

本文共 2544 字,大约阅读时间需要 8 分钟。

在中,写了一个在特殊情况下,也就是只有一个读线程和一个写线程的情况下,的无锁队列的实现。其中甚至都没有利用特殊的原子加减操作,只是普通的运算。这样做的原因是,即使是特殊的原子加减操作,也比普通的加减运算复杂度高很多。因此文中的实现方法可以达到很高的运行效率。

但是,有的情况下并不是只有一个读线程和一个写线程。越是一般化的实现,支持的情况越多,但是往往损失的性能也越多。作者看到过一个实现(),可以实现一个读线程,多个写线程,或者相反,一个写线程,多个读线程。这篇文章中作者采用了原子加减的操作。所以这样的实现的运行效率会稍有点低。那么,如果情况稍特殊一点,比如,有一个线程读,两个线程写,这时可以有一个特殊的实现能够达到很高的效率吗?作者折腾了一番,找到了一个方法。

原理如下图所示。 

基本原理是,将整个buffer分成两份,两个写线程分别写入其中的一部分。这样就避免了两个写线程之间的冲突。而避免读线程和写线程之间冲突的原理,则和中的原理相同,也就是,写线程只修改tail的值,而读线程只修改head的值。这样,就不会出现数据还没读就被覆盖,或者数据还没写就被读出的情况了。

这样的实现有一些缺点。一个是空间利用率不够高,会有浪费,因为有可能一部分写满了而另外一部分还空着;其次,是不能保证读出的顺序和写入的顺序是一致的。不过,实际上有两个线程写的时候,这点本来就不重要。没办法保证那个线程先写,哪个后写。最后,在这个实现中,是buffer的两个部分轮流读数据。这个策略可以根据两个写线程的数据速率进行调整。

但是,这个实现有一个最大的好处,就是速度快。同样没有采用原子加减操作,而只是普通的加减操作。因此实现了很高的运行速度。在符合两个写线程,一个读线程,并且对运行速度有很高要求的场合中,这个实现是一个很好的选择。

最后,附上代码。代码同样可以在github上找到

 

1 public class RingBuffer { 2     private final static int bufferSize = 1024; 3     private final static int halfBufferSize = bufferSize / 2; 4     private String[] buffer = new String[bufferSize]; 5     private int head1 = 0; 6     private int tail1 = 0; 7     private int head2 = 0; 8     private int tail2 = 0; 9     private int nextReadBuffer = 0;10     11     private Boolean empty1() {12         return head1 == tail1;13     }14     private Boolean empty2() {15         return head2 == tail2;16     }17     private Boolean empty() {18         return empty1() && empty2();19     }20     private Boolean full1() {21         return (tail1 + 1) % halfBufferSize == head1;22     }23     private Boolean full2() {24         return (tail2 + 1) % halfBufferSize == head2;25     }26     public Boolean put1(String v) {27         if (full1()) {28             return false;29         }30         buffer[tail1] = v;31         tail1 = (tail1 + 1) % halfBufferSize;32         return true;33     }34     public Boolean put2(String v) {35         if (full2()) {36             return false;37         }38         buffer[tail2 + halfBufferSize] = v;39         tail2 = (tail2 + 1) % halfBufferSize;40         return true;41     }42     public String get() {43         if (empty()) {44             return null;45         }46         String result = null;47         if (nextReadBuffer == 0 && !empty1() || nextReadBuffer == 1 && empty2()) {48             result = buffer[head1];49             head1 = (head1 + 1) % halfBufferSize;50         } else {51             result = buffer[head2 + halfBufferSize];52             head2 = (head2 + 1) % halfBufferSize;53         }54         55         nextReadBuffer = (nextReadBuffer + 1) % 2;56 57         return result;58     }59 }

转载地址:http://mugmb.baihongyu.com/

你可能感兴趣的文章
用SQL删除数据(转)
查看>>
用SQL进行嵌套查询(转)
查看>>
用SQL进行单表查询(转)
查看>>
用SQL进行多表查询(转)
查看>>
Oracle 9i管理的模式(转)
查看>>
Oracle 9i管理的用户(转)
查看>>
Oracle 9i管理工具的使用(转)
查看>>
网络关系型数据库的代表Oracle 9i(转)
查看>>
安装管理客户机(转)
查看>>
目前主流的两类关系型数据库系统(转)
查看>>
Oracle 9i的两种工作模式(转)
查看>>
在Oracle数据库10g中跟踪SQL(转)
查看>>
Oracle 10g Release2新功能之变化通知(转)
查看>>
ORACLE之常用FAQ V1.0一(构架体系)(转)
查看>>
Oracle 10g 新特性之虚拟专用数据库(转)
查看>>
深刻理解Oracle数据库的启动和关闭(转)
查看>>
将Oracle 10g内置的安全特性用于PHP(转)
查看>>
Oracle 8i中字符集乱码问题析及其解决办法(转)
查看>>
Oracle 9i产品文档(转)
查看>>
Oracle数据库处理多媒体信息(转)
查看>>