zoukankan      html  css  js  c++  java
  • 线程安全的无锁RingBuffer的实现

    在程序设计中,我们有时会遇到这样的情况,一个线程将数据写到一个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的线程安全性。

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

    复制代码
     1 public class RingBuffer {
     2 
     3     private final static int bufferSize = 1024;
     4     private String[] buffer = new String[bufferSize];
     5     private int head = 0;
     6     private int tail = 0;
     7     
     8     private Boolean empty() {
     9         return head == tail;
    10     }
    11     private Boolean full() {
    12         return (tail + 1) % bufferSize == head;
    13     }
    14     public Boolean put(String v) {
    15         if (full()) {
    16             return false;
    17         }
    18         buffer[tail] = v;
    19         tail = (tail + 1) % bufferSize;
    20         return true;
    21     }
    22     public String get() {
    23         if (empty()) {
    24             return null;
    25         }
    26         String result = buffer[head];
    27         head = (head + 1) % bufferSize;
    28         return result;
    29     }
    30     public String[] getAll() {
    31         if (empty()) {
    32             return new String[0];
    33         }
    34         int copyTail = tail;
    35         int cnt = head < copyTail ? copyTail - head : bufferSize - head + copyTail;
    36         String[] result = new String[cnt];
    37         if (head < copyTail) {
    38             for (int i = head; i < copyTail; i++) {
    39                 result[i - head] = buffer[i];
    40             }
    41         } else {
    42             for (int i = head; i < bufferSize; i++) {
    43                 result[i - head] = buffer[i];
    44             }
    45             for (int i = 0; i < copyTail; i++) {
    46                 result[bufferSize - head + i] = buffer[i];
    47             }
    48         }
    49         head = copyTail;
    50         return result;
    51     }
    52 }
    复制代码
  • 相关阅读:
    向架构师进军--->如何编写软件架构文档
    让创意更有黏性!
    eaby技术架构变迁
    应用系统之间数据传输的几种方式
    基于 CAS 无锁实现的 Disruptor.NET 居然慢于 BlockingCollection,是真的吗?
    调整数据库表结构,搞定 WordPress 数据库查询缓慢问题
    dynamic-css 动态 CSS 库,使得你可以借助 MVVM 模式动态生成和更新 css,从 js 事件和 css 选择器的苦海中脱离出来
    ASP.NET Framework 重写后的 .NET 异常报错界面(异常堆栈和溯源一目了然)
    Orchard Core 中运行带程序上下文的单元测试
    Angular 2 前端 http 传输 model 对象及其外键的问题
  • 原文地址:https://www.cnblogs.com/hwl1023/p/4946372.html
Copyright © 2011-2022 走看看