在程序设计中,我们有时会遇到这样的情况,一个线程将数据写到一个buffer中,另外一个线程从中读数据。所以这里就有多线程竞争的问题。通常的解决办法是对竞争资源加锁。但是,一般加锁的损耗较高。其实,对于这样的一个线程写,一个线程读的特殊情况,可以以一种简单的无锁RingBuffer来实现。这样代码的运行效率很高。

本文借鉴了Disruptor项目代码。

代码我在github上放了一份,需要的同学可以去下载(RingBuffer.java)。本文最后也会附上一份。

代码的基本原理如下。

如图所示,假定buffer的长度是bufferSize. 我们设置两个指针。head指向的是下一次读的位置,而tail指向的是下一次写的位置。由于这里是环形buffer (ring buffer),这里就有一个问题,怎样判断buffer是满或者空。这里采用的规则是,buffer的最后一个单元不存储数据。所以,如果head == tail,那么说明buffer为空。如果 head == tail + 1 (mod bufferSize),那么说明buffer满了。

接下来就是最重要的内容了:怎样以无锁的方式进行线程安全的buffer的读写操作。基本原理是这样的。在进行读操作的时候,我们只修改head的值,而在写操作的时候我们只修改tail的值。在写操作时,我们在写入内容到buffer之后才修改tail的值;而在进行读操作的时候,我们会读取tail的值并将其赋值给copyTail。赋值操作是原子操作。所以在读到copyTail之后,从head到copyTail之间一定是有数据可以读的,不会出现数据没有写入就进行读操作的情况。同样的,读操作完成之后,才会修改head的数值;而在写操作之前会读取head的值判断是否有空间可以用来写数据。所以,这时候tail到head - 1之间一定是有空间可以写数据的,而不会出现一个位置的数据还没有读出就被写操作覆盖的情况。这样就保证了RingBuffer的线程安全性。

最后附上代码供参考。欢迎批评指正,也欢迎各种讨论!

 public class RingBuffer {

     private final static int bufferSize = 1024;
private String[] buffer = new String[bufferSize];
private int head = 0;
private int tail = 0; private Boolean empty() {
return head == tail;
}
private Boolean full() {
return (tail + 1) % bufferSize == head;
}
public Boolean put(String v) {
if (full()) {
return false;
}
buffer[tail] = v;
tail = (tail + 1) % bufferSize;
return true;
}
public String get() {
if (empty()) {
return null;
}
String result = buffer[head];
head = (head + 1) % bufferSize;
return result;
}
public String[] getAll() {
if (empty()) {
return new String[0];
}
int copyTail = tail;
int cnt = head < copyTail ? copyTail - head : bufferSize - head + copyTail;
String[] result = new String[cnt];
if (head < copyTail) {
for (int i = head; i < copyTail; i++) {
result[i - head] = buffer[i];
}
} else {
for (int i = head; i < bufferSize; i++) {
result[i - head] = buffer[i];
}
for (int i = 0; i < copyTail; i++) {
result[bufferSize - head + i] = buffer[i];
}
}
head = copyTail;
return result;
}
}

线程安全的无锁RingBuffer的实现【一个读线程,一个写线程】的更多相关文章

  1. 线程安全的无锁RingBuffer的实现

    这里的线程安全,是指一个读线程和一个写线程,读写两个线程是安全的,而不是说多个读线程和多个写线程是安全的.. 在程序设计中,我们有时会遇到这样的情况,一个线程将数据写到一个buffer中,另外一个线程 ...

  2. 高效线程池之无锁化实现(Linux C)

    from:http://blog.csdn.net/xhjcehust/article/details/45844901 笔者之前练手写过一个小的线程池版本(已上传至https://github.co ...

  3. (转)高效线程池之无锁化实现(Linux C)

    本文链接:https://blog.csdn.net/xhjcehust/article/details/45844901 笔者之前照着通用写法练手写过一个小的线程池版本,最近几天复习了一下,发现大多 ...

  4. Erlang运行时中的无锁队列及其在异步线程中的应用

    本文首先介绍 Erlang 运行时中需要使用无锁队列的场合,然后介绍无锁队列的基本原理及会遇到的问题,接下来介绍 Erlang 运行时中如何通过“线程进度”机制解决无锁队列的问题,并介绍 Erlang ...

  5. 一个无锁消息队列引发的血案(四)——月:RingQueue(上) 自旋锁

    目录 (一)起因 (二)混合自旋锁 (三)q3.h 与 RingBuffer (四)RingQueue(上) 自旋锁 (五)RingQueue(中) 休眠的艺术 (六)RingQueue(中) 休眠的 ...

  6. Boost无锁队列

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/okiwilldoit/article/details/50970408 在开发接收转发agent时, ...

  7. 实现无锁的栈与队列(5):Hazard Pointer

    两年多以前随手写了点与 lock free 相关的笔记:1,2,3,4,质量都不是很高其实(读者见谅),但两年来陆陆续续竟也有些阅读量了(可见剑走偏锋的技巧是多容易吸引眼球).笔记当中在解决内存释放和 ...

  8. 基于无锁的C#并发队列实现(转载)

    最近开始学习无锁编程,和传统的基于Lock的算法相比,无锁编程具有其独特的优点,Angel Lucifer的关于无锁编程一文对此有详细的描述. 无锁编程的目标是在不使用Lock的前提下保证并发过程中共 ...

  9. Java高并发之无锁与Atomic源码分析

    目录 CAS原理 AtomicInteger Unsafe AtomicReference AtomicStampedReference AtomicIntegerArray AtomicIntege ...

随机推荐

  1. HTML 5 应用程序缓存(Application Cache)cache manifest 文件使用 html5 中创建manifest缓存以及更新方法 一个manifest文件会创建一份缓存,不同的manifest文件其缓存的内容是互不干扰的

    HTML5 离线缓存-manifest简介 HTML 5 应用程序缓存 使用 HTML5,通过创建 cache manifest 文件,可以轻松地创建 web 应用的离线版本. 什么是应用程序缓存(A ...

  2. dataset string dataset

    DataSet ds = new DataSet(); ds.ReadXml(dataFile); Console.WriteLine(data.Length); string dataxml = d ...

  3. 定制jQuery File Upload为微博式单文件上传

    日志未经声明,均为AlloVince原创.版权采用『 知识共享署名-非商业性使用 2.5 许可协议』进行许可. jQuery File Upload是一个非常优秀的上传组件,主要使用了XHR作为上传方 ...

  4. android 控件各种颜色的半透明效果配置

    格式: android:background="#XXxxxxxx"(颜色可以写在color中) 说明:半透明颜色值不同于平时使用的颜色,半透明颜色值共8位,前2位是透明度,后6位 ...

  5. 【转】对 Parser 的误解

    一直很了解人们对于parser的误解,可是一直都提不起兴趣来阐述对它的观点.然而我觉得是有必要解释一下这个问题的时候了.我感觉得到大部分人对于parser的误解之深,再不澄清一下,恐怕这些谬误就要写进 ...

  6. sql 置顶功能的查询

    sql中有置顶的需求,文章很多条,分页查询,要求置顶的在最前面: 只需要使用: order by 置顶字段 即可

  7. [转]Http Message结构学习总结

    最近做的东西需要更深入地了解Http协议,故死磕了一下RFC2616-HTTP/1.1协议,主要是了解Http Message结构及每部分含义,在此总结一下,写一个模拟发送HTTP请求的工具,由于时间 ...

  8. 纯干货,Spring-data-jpa详解(转)

    本篇进行Spring-data-jpa的介绍,几乎涵盖该框架的所有方面,在日常的开发当中,基本上能满足所有需求.这里不讲解JPA和Spring-data-jpa单独使用,所有的内容都是在和Spring ...

  9. FIR调用DSP48E_05

    作者:桂. 时间:2018-02-06  17:52:38 链接:http://www.cnblogs.com/xingshansi/p/8423457.html 前言 到目前为止,本文没有对滤波器实 ...

  10. 关于python中__name__=='__main__'的解释

    调用文章是:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014318451 ...