java vector 锁_[Java教程]【实战Java高并发程序设计6】挑战无锁算法:无锁的Vector实现...

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

0 2016-02-29 18:00:06

【实战Java高并发程序设计 1】Java中的指针:Unsafe类

【实战Java高并发程序设计 2】无锁的对象引用:AtomicReference

【实战Java高并发程序设计 3】带有时间戳的对象引用:AtomicStampedReference

【实战Java高并发程序设计 4】数组也能无锁:AtomicIntegerArray

【实战Java高并发程序设计 5】让普通变量也享受原子操作

我们已经比较完整得介绍了有关无锁的概念和使用方法。相对于有锁的方法,使用无锁的方式编程更加考验一个程序员的耐心和智力。但是,无锁带来的好处也是显而易见的,第一,在高并发的情况下,它比有锁的程序拥有更好的性能;第二,它天生就是死锁免疫的。就凭借这2个优势,就值得我们冒险尝试使用无锁的并发。

这里,我想向大家介绍一种使用无锁方式实现的Vector。通过这个案例,我们可以更加深刻地认识无锁的算法,同时也可以学习一下有关Vector实现的细节和算法技巧。(在本例中,讲述的无锁Vector来自于amino并发包)

我们将这个无锁的Vector称为LockFreeVector。它的特点是可以根据需求动态扩展其内部空间。在这里,我们使用二维数组来表示LockFreeVector的内部存储,如下:private final AtomicReferenceArray> buckets;

变量buckets存放所有的内部元素。从定义上看,它是一个保存着数组的数组,也就是通常的二维数组。特别之处在于这些数组都是使用CAS的原子数组。为什么使用二维数组去实现一个一维的Vector呢?这是为了将来Vector进行动态扩展时可以更加方便。我们知道,AtomicReferenceArray内部使用Object[]来进行实际数据的存储,这使得动态空间增加特别的麻烦,因此使用二维数组的好处就是为将来增加新的元素。

此外,为了更有序的读写数组,定义一个称为Descriptor的元素。它的作用是使用CAS操作写入新数据。01 static class Descriptor {02 public int size;03 volatile WriteDescriptor writeop;04 public Descriptor(int size, WriteDescriptor writeop) {05 this.size = size;06 this.writeop = writeop;07 }08 public void completeWrite() {09 WriteDescriptor tmpOp = writeop;10 if (tmpOp != null) {11 tmpOp.doIt();12 writeop = null; // this is safe since all write to writeop use13 // null as r_value.14 }15 }16 }1718 static class WriteDescriptor {19 public E oldV;20 public E newV;21 public AtomicReferenceArray addr;22 public int addr_ind;2324 public WriteDescriptor(AtomicReferenceArray addr, int addr_ind,25 E oldV, E newV) {26 this.addr = addr;27 this.addr_ind = addr_ind;28 this.oldV = oldV;29 this.newV = newV;30 }3132 public void doIt() {33 addr.compareAndSet(addr_ind, oldV, newV);34 }35 }

上述代码第4行定义的Descriptor构造函数接收2个参数,第一个为整个Vector的长度,第2个为一个writer。最终,写入数据是通过writer进行的(通过completeWrite()方法)。

第24行,WriteDescriptor的构造函数接收4个参数。第一个参数addr表示要修改的原子数组,第二个参数为要写入的数组索引位置,第三个oldV为期望值,第4个newV为需要写入的值。

在构造LockFreeVector时,显然需要将buckets和descriptor进行初始化。public LockFreeVector() { buckets = new AtomicReferenceArray>(N_BUCKET); buckets.set(0, new AtomicReferenceArray(FIRST_BUCKET_SIZE)); descriptor = new AtomicReference>(new Descriptor(0, null));}

在这里N_BUCKET为30,也就是说这个buckets里面可以存放一共30个数组(由于数组无法动态增长,因此数组总数也就不能超过30个)。并且将第一个数组的大小为FIRST_BUCKET_SIZE为8。到这里,大家可能会有一个疑问,如果每个数组8个元素,一共30个数组,那岂不是一共只能存放240个元素吗?

如果大家了解JDK内的Vector实现,应该知道,Vector在进行空间增长时,默认情况下,每次都会将总容量翻倍。因此,这里也借鉴类似的思想,每次空间扩张,新的数组的大小为原来的2倍(即每次空间扩展都启用一个新的数组),因此,第一个数组为8,第2个就是16,第3个就是32。以此类推,因此30个数组可以支持的总元素达到。

这数值已经超过了2^33,即在80亿以上。因此,可以满足一般的应用。

当有元素需要加入LockFreeVector时,使用一个名为push_back()的方法,将元素压入Vector最后一个位置。这个操作显然就是LockFreeVector的最为核心的方法,也是最能体现CAS使用特点的方法,它的实现如下:01 public void push_back(E e) {02 Descriptor desc;03 Descriptor newd;04 do {05 desc = descriptor.get();06 desc.completeWrite();0708 int pos = desc.size + FIRST_BUCKET_SIZE;09 int zeroNumPos = Integer.numberOfLeadingZeros(pos);10 int bucketInd = zeroNumFirst - zeroNumPos;11 if (buckets.get(bucketInd) == null) {12 int newLen = 2 * buckets.get(bucketInd - 1).length();13 if (debug)14 System.out.println("New Length is:" + newLen);15 buckets.compareAndSet(bucketInd, null,16 new AtomicReferenceArray(newLen));17 }1819 int idx = (0x80000000>>>zeroNumPos) ^ pos;20 newd = new Descriptor(desc.size + 1, new WriteDescriptor(21 buckets.get(bucketInd), idx, null, e));22 } while (!descriptor.compareAndSet(desc, newd));23 descriptor.get().completeWrite();24 }

可以看到,这个方法主体部分是一个do-while循环,用来不断尝试对descriptor的设置。也就是通过CAS保证了descriptor的一致性和安全性。在第23行,使用descriptor将数据真正地写入数组中。这个descriptor写入的数据由20~21行构造的WriteDescriptor决定。

摘自《实战Java高并发程序设计》

bc91bb04e6e9c61e24c974e4440db8f2.gif

本文网址:http://www.shaoqun.com/a/197387.html

*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们:admin@shaoqun.com。

JAVA

0

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值