线程安全的无锁RingBuffer的实现【一个读线程,一个写线程】

时间:2022-09-23 10:19:48

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

本文借鉴了Disruptor项目代码。

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

代码的基本原理如下。

线程安全的无锁RingBuffer的实现【一个读线程,一个写线程】

如图所示,假定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. 高效线程池之无锁化实现&lpar;Linux C&rpar;

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

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

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

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

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

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

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

  6. Boost无锁队列

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

  7. 实现无锁的栈与队列&lpar;5&rpar;:Hazard Pointer

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

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

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

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

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

随机推荐

  1. git 指令

    單獨看這個 commit id 做了什麼修改 git show CommitID or git diff CommitID^!

  2. 将halcon导出的c&plus;&plus;程序打包成dll库

     1.从“文件”菜单中,选择“新建”,然后选择“项目…”. 2.从“项目类型”窗格中选择“Visual C++”下的“Win32”. 3.从“模板”窗格中,选择“Win32 控制台应用程序”. 4.为 ...

  3. SparkGraphXTest&period;scala

    /** * Created by root on 9/8/15. */ import org.apache.spark._ import org.apache.spark.graphx._ impor ...

  4. 使用POI生成Excel报表

    先把报表模板截图贴上来 下面是POI编写的报表生成类ExcelReport.java package com.jadyer.report; import java.io.FileNotFoundExc ...

  5. HTML URL编码参考&lpar;转载&rpar;

    URL编码(URL encoding)的作用是将字符转化为可在因特网上安全传输的格式.URL——统一资源定位符Web浏览器通过URL从Web服务器上请求页面.URL就是网页的地址,如:http://w ...

  6. 前端笔记---塌陷top

    一.在设置盒子div的子元素的外边框margin-top,子元素属性不起作用,父元素下沉: <!DOCTYPE html> <html lang="en"> ...

  7. 无线渗透测试之wifi密码破解

    [声明]:本文仅供个人学习使用,请勿违法破解他人wifi 测试工具: 1.CDlinux启动盘:(请参照https://my.oschina.net/u/3112136/blog/800713) 2. ...

  8. linux command ------ find

    find | grep luoluo 将当前目录及子目录的文件名中含有luoluo的文件过滤出来 | 是管道,把find查找的内容传递给下个命令(grep) find后面不加其他内容(比如路径)表示打 ...

  9. 开源方案搭建可离线的精美矢量切片地图服务-7&period;Arcgis Pro企业级应用

    1.前言 上篇讲.pbf字体库的时候说到我们使用的字体通过Arcgis Pro 生成,Arcgis Pro样式基于Mapbox做的矢量切片地图渲染.这篇主要讲一下Arcgis Pro矢量切片生成的的具 ...

  10. 【java排序】冒泡排序、快速排序

    冒泡排序 冒泡排序是一种简单的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地 进行直到没有再需要交换,也就是说该数列已经排序完成.这 ...