这两天学习无锁的并发模式,发现相比于传统的 同步加锁相比,有两点好处
1.无锁 模式 相比于 传统的 同步加锁  提高了性能

2.无锁模式 是天然的死锁免疫

下来介绍无锁的Vector--- LockFreeVector

它的结构是:

private final AtomicReferenceArray<AtomicReferenceArray<E>> buckets;

从这里我们可以看到,它的内部是采用的是 无锁的引用数组, 数组嵌套数组

相当于一个二维数组,它的大小可以动态的进行扩展,

为了更有序的读写数组,定义了一个Descriptor的静态内部类。它的作用是使用CAS操作写入新数据。

它定义了

private static final int FIRST_BUCKET_SIZE = 8;

/**
* number of buckets. 30 will allow 8*(2^30-1) elements
*/
private static final int N_BUCKET = 30;
FIRST_BUCKET_SIZE:为第一个数组的长度

N_BUCKET 整个二维数组最大可扩转至30

每次的扩展是成倍的增加,即:第一个数组长度为8,第二个为8<<1,第三个为8<<2 ......第30个为 8<<29

贡献源码:

/*
* Copyright (c) 2007 IBM Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package main.java.org.amino.ds.lockfree; import java.util.AbstractList;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray; /**
* It is a thread safe and lock-free vector.
* This class implement algorithm from:<br>
*
* Lock-free Dynamically Resizable Arrays <br>
*
* Damian Dechev, Peter Pirkelbauer, and Bjarne Stroustrup<br>
* Texas A&M University College Station, TX 77843-3112<br>
* {dechev, peter.pirkelbauer}@tamu.edu, bs@cs.tamu.edu
*
*
* @author Zhi Gan
*
* @param <E> type of element in the vector
*
*/
public class LockFreeVector<E> extends AbstractList<E> {
private static final boolean debug = false;
/**
* Size of the first bucket. sizeof(bucket[i+1])=2*sizeof(bucket[i])
*/
private static final int FIRST_BUCKET_SIZE = 8; /**
* number of buckets. 30 will allow 8*(2^30-1) elements
*/
private static final int N_BUCKET = 30; /**
* We will have at most N_BUCKET number of buckets. And we have
* sizeof(buckets.get(i))=FIRST_BUCKET_SIZE**(i+1)
*/
private final AtomicReferenceArray<AtomicReferenceArray<E>> buckets; /**
* @author ganzhi
*
* @param <E>
*/
static class WriteDescriptor<E> {
public E oldV;
public E newV;
public AtomicReferenceArray<E> addr;
public int addr_ind; /**
* Creating a new descriptor.
*
* @param addr Operation address
* @param addr_ind Index of address
* @param oldV old operand
* @param newV new operand
*/
public WriteDescriptor(AtomicReferenceArray<E> addr, int addr_ind,
E oldV, E newV) {
this.addr = addr;
this.addr_ind = addr_ind;
this.oldV = oldV;
this.newV = newV;
} /**
* set newV.
*/
public void doIt() {
addr.compareAndSet(addr_ind, oldV, newV);
}
} /**
* @author ganzhi
*
* @param <E>
*/
static class Descriptor<E> {
public int size;
volatile WriteDescriptor<E> writeop; /**
* Create a new descriptor.
*
* @param size Size of the vector
* @param writeop Executor write operation
*/
public Descriptor(int size, WriteDescriptor<E> writeop) {
this.size = size;
this.writeop = writeop;
} /**
*
*/
public void completeWrite() {
WriteDescriptor<E> tmpOp = writeop;
if (tmpOp != null) {
tmpOp.doIt();
writeop = null; // this is safe since all write to writeop use
// null as r_value.
}
}
} private AtomicReference<Descriptor<E>> descriptor;
private static final int zeroNumFirst = Integer
.numberOfLeadingZeros(FIRST_BUCKET_SIZE);; /**
* Constructor.
*/
public LockFreeVector() {
buckets = new AtomicReferenceArray<AtomicReferenceArray<E>>(N_BUCKET);
buckets.set(0, new AtomicReferenceArray<E>(FIRST_BUCKET_SIZE));
descriptor = new AtomicReference<Descriptor<E>>(new Descriptor<E>(0,
null));
} /**
* add e at the end of vector.
*
* @param e
* element added
*/
public void push_back(E e) {
Descriptor<E> desc;
Descriptor<E> newd;
do {
desc = descriptor.get();
desc.completeWrite();
//desc.size Vector 本身的大小
//FIRST_BUCKET_SIZE 第一个一位数组的大小
int pos = desc.size + FIRST_BUCKET_SIZE;
int zeroNumPos = Integer.numberOfLeadingZeros(pos); // 取出pos 的前导领
//zeroNumFirst 为FIRST_BUCKET_SIZE 的前导领
int bucketInd = zeroNumFirst - zeroNumPos; //哪个一位数组
//判断这个一维数组是否已经启用
if (buckets.get(bucketInd) == null) {
//newLen 一维数组的长度
int newLen = 2 * buckets.get(bucketInd - 1).length();
if (debug)
System.out.println("New Length is:" + newLen);
buckets.compareAndSet(bucketInd, null,
new AtomicReferenceArray<E>(newLen));
} int idx = (0x80000000>>>zeroNumPos) ^ pos; //在这个一位数组中,我在哪个位置
newd = new Descriptor<E>(desc.size + 1, new WriteDescriptor<E>(
buckets.get(bucketInd), idx, null, e));
} while (!descriptor.compareAndSet(desc, newd));
descriptor.get().completeWrite();
} /**
* Remove the last element in the vector.
*
* @return element removed
*/
public E pop_back() {
Descriptor<E> desc;
Descriptor<E> newd;
E elem;
do {
desc = descriptor.get();
desc.completeWrite(); int pos = desc.size + FIRST_BUCKET_SIZE - 1;
int bucketInd = Integer.numberOfLeadingZeros(FIRST_BUCKET_SIZE)
- Integer.numberOfLeadingZeros(pos);
int idx = Integer.highestOneBit(pos) ^ pos;
elem = buckets.get(bucketInd).get(idx);
newd = new Descriptor<E>(desc.size - 1, null);
} while (!descriptor.compareAndSet(desc, newd)); return elem;
} /**
* Get element with the index.
*
* @param index
* index
* @return element with the index
*/
@Override
public E get(int index) {
int pos = index + FIRST_BUCKET_SIZE;
int zeroNumPos = Integer.numberOfLeadingZeros(pos);
int bucketInd = zeroNumFirst - zeroNumPos;
int idx = (0x80000000>>>zeroNumPos) ^ pos;
return buckets.get(bucketInd).get(idx);
} /**
* Set the element with index to e.
*
* @param index
* index of element to be reset
* @param e
* element to set
*/
/**
* {@inheritDoc}
*/
public E set(int index, E e) {
int pos = index + FIRST_BUCKET_SIZE;
int bucketInd = Integer.numberOfLeadingZeros(FIRST_BUCKET_SIZE)
- Integer.numberOfLeadingZeros(pos);
int idx = Integer.highestOneBit(pos) ^ pos;
AtomicReferenceArray<E> bucket = buckets.get(bucketInd);
while (true) {
E oldV = bucket.get(idx);
if (bucket.compareAndSet(idx, oldV, e))
return oldV;
}
} /**
* reserve more space.
*
* @param newSize
* new size be reserved
*/
public void reserve(int newSize) {
int size = descriptor.get().size;
int pos = size + FIRST_BUCKET_SIZE - 1;
int i = Integer.numberOfLeadingZeros(FIRST_BUCKET_SIZE)
- Integer.numberOfLeadingZeros(pos);
if (i < 1)
i = 1; int initialSize = buckets.get(i - 1).length();
while (i < Integer.numberOfLeadingZeros(FIRST_BUCKET_SIZE)
- Integer.numberOfLeadingZeros(newSize + FIRST_BUCKET_SIZE - 1)) {
i++;
initialSize *= FIRST_BUCKET_SIZE;
buckets.compareAndSet(i, null, new AtomicReferenceArray<E>(
initialSize));
}
} /**
* size of vector.
*
* @return size of vector
*/
public int size() {
return descriptor.get().size;
} /**
* {@inheritDoc}
*/
@Override
public boolean add(E object) {
push_back(object);
return true;
}
}

参考:http://www.shaoqun.com/a/197387.aspx

源码部分,我只着重写了push_back这个方法的注释。

无锁模式的Vector的更多相关文章

  1. CAS实现无锁模式

    用多线程实现一个数字的自增长到1000000,分别用无锁模式和锁模式来实现代码. 1.使用ReentrantLock. package test; import java.util.concurren ...

  2. CAS无锁模式

    一.java内存模型:JMM 在内存模型当中定义一个主内存,所有声明的实例变量都存在于主内存当中,主内存的数据会共享给所有线程,每一个线程有一个块工作内存,工作内存当中主内存数据的副本当更新数据时,会 ...

  3. CAS无锁机制原理

    原子类 java.util.concurrent.atomic包:原子类的小工具包,支持在单个变量上解除锁的线程安全编程 原子变量类相当于一种泛化的 volatile 变量,能够支持原子的和有条件的读 ...

  4. 无锁版以时间为GUID的方法

    之前的博客 将时间作为GUID的方法 中,我使用了锁.我在实际的使用中,错将锁的释放放在了if语句中,这纯粹是我的失误,导致了很严重的错误.因此我在想是否有无锁的将时间作为GUID的方式,答案是使用I ...

  5. 【实战Java高并发程序设计6】挑战无锁算法:无锁的Vector实现

    [实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...

  6. 性能优化-使用双buffer实现无锁队列

    借助本文,实现一种在"读多写一"场景下的无锁实现方式 在我们的工作中,多线程编程是一件太稀松平常的事.在多线程环境下操作一个变量或者一块缓存,如果不对其操作加以限制,轻则变量值或者 ...

  7. [转]透过 Linux 内核看无锁编程

    非阻塞型同步 (Non-blocking Synchronization) 简介 如何正确有效的保护共享数据是编写并行程序必须面临的一个难题,通常的手段就是同步.同步可分为阻塞型同步(Blocking ...

  8. 非阻塞同步算法与CAS(Compare and Swap)无锁算法

    锁(lock)的代价 锁是用来做并发最简单的方式,当然其代价也是最高的.内核态的锁的时候需要操作系统进行一次上下文切换,加锁.释放锁会导致比较多的上下文切换和调度延时,等待锁的线程会被挂起直至锁释放. ...

  9. 无锁编程以及CAS

    无锁编程 / lock-free / 非阻塞同步 无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Sy ...

随机推荐

  1. C/C++中数组与指针的关系探究

    数组与指针 长期以来,在C/C++中,数组名和指向数组首元素的指针常量到底是以一种什么关系,一直困扰着很多人.很多地方,甚至是一些教科书中都在说,"数组名就是一个指向数组首元素的指针常量&q ...

  2. 你绝对想不到R文件找不到(cannot resolve symbol R)的原因

    你绝对想不到R文件找不到(cannot resolve symbol R)的原因 最近在项目开发中 Android Studio 的 R 文件突然找不到了.IDE 中出现了以下提示 cannot re ...

  3. RHEL7对比RHEL6的主要变化

    RHEL7和RHEL6的主要变化 RHEL7和RHEL6的主要变化   RHEL7 RHEL6 文件系统 XFS EXT4 内核版本 3.10.x-x系列 2.6.x-x系列 内核名称 Maipo S ...

  4. js中字符串转换为日期型

    简介:字符串转日期型函数 传入一个字符串格式的日期,如何转换为日期型的.以下为转换方案. //字符串转换为日期函数,返回日期型(传入的日期格式2014-04-22) function StringTo ...

  5. ES6 深入let的作用域

    说到ES6的let变量声明,我估计很多人会想起下面几个主要的特点: 没有变量声明提升 拥有块级作用域 暂时死区 不能重复声明 很多教程和总结基本都说到了这几点(说实话大部分文章都大同小异,摘录的居多) ...

  6. 几个地图(高德、百度、Apple、Google)URL API

    移动应用中,如何在自己的App中调起第三方的原生地图App,并显示相关的信息,如显示指定的一个坐标位置,显示一个起点到终点的路线查询,等等. 目前几个主要的地图商都提供了自己的App通过URL调用的形 ...

  7. 论.net平台的切身感触(惑)

    这篇博客只是作者客观看法,不喜勿喷,条条大路通罗马,路不同风景也不一样,接下来的路该怎么走? 简介:作者.net程序员一枚,工作已有四年,接触过.net平台winform,webform,mvc的开发 ...

  8. Latex 公式在线可视化编辑器

    寻觅 最近的一个demo需要用到Latex公式在线编辑器,从搜索引擎一般会得到类似http://latex.codecogs.com/eqneditor/editor.php的结果,这个编辑器的问题在 ...

  9. 1.6 在WHERE子句中引用取别名的列

    如下查询,会抛出错误:mysql> select sal as salary, comm as commission from emp where salary < 5000;ERROR ...

  10. 《JavaScript面向对象编程指南(第2版)》读书笔记(一)

    目录 一.对象 1.1 获取属性值的方式 1.2 获取动态生成的属性的值 二.数组 2.1 检测是否为数组 2.2 增加数组长度导致未赋值的位置为undefined 2.3 用闭包实现简易迭代器 三. ...