博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java并发编程(十四): 原子变量与非阻塞同步机制
阅读量:6589 次
发布时间:2019-06-24

本文共 5016 字,大约阅读时间需要 16 分钟。

hot3.png

原子变量与非阻塞同步机制:

锁的优势:

  • 当线程在锁上发生竞争时,智能的JVM不一定会挂起线程,而是根据之前获取操作中对锁持有时间来判断此线程是挂起还是自旋

硬件对并发的影响:

  • 独占锁是一项悲观技术。
  • 现在处理器基本都支持一些读-写-改的指令,如比较并交换(CAS)关联加载/条件存储(Load-Linked/Store-Conditional)。操作系统和JVM使用这些指令来实现锁和并发数据结构。jdk5之前不能直接使用这些指令

比较并交换:

  • CAS包含了3个操作数--需要读写的内存位置V, 进行比较的值A拟写入的新值B
  • 一个模拟的CAS操作:
/** * 模拟CAS */public class SimulatedCAS {	private int value;		public synchronized int get(){		return value;	}		public synchronized int compareAndSwap(int expectedValue, int newValue){		int oldValue = value;		if (oldValue == expectedValue){			value = newValue;		}		return oldValue;	}		public synchronized boolean compareAndSet(int expectedValue, int newValue){		return (expectedValue == compareAndSwap(expectedValue, newValue));	}}

非阻塞的计数器:

/** * 基于CAS实现的非阻塞计数器 */public class CasCounter {	private SimulatedCAS value;		public int getValue(){		return value.get();	}		public int increment(){		int v;				do {			v = value.get();		} while (v != value.compareAndSwap(v, v+1));		return v + 1;	}}
  • 在大多数处理器上,在无竞争的锁获取与释放的"快速代码路径"上的开销,大约CAS开销的两倍。

JVM对CAS的支持:

  • JVM为数字类型和引用类型提供了一些高效的CAS操作,如AtomicXxx。

原子变量类:

原子变量是一种"更好的volatile":

/** * 通过CAS来维持包含多个变量的不变性条件 */public class CasNumberRange {	private static class IntPair{		// 不变性条件: lower <= upper		final int lower;		final int upper;				public IntPair(int lower, int upper) {			this.lower = lower;			this.upper = upper;		}	}		private AtomicReference
values = new AtomicReference<>(); public int getLower(){ return values.get().lower; } public int getUpper(){ return values.get().upper; } public void setLower(int i){ while (true){ IntPair oldv = values.get(); if (i > oldv.upper){ throw new IllegalArgumentException("lower can't > upper"); } IntPair newv = new IntPair(i, oldv.upper); if (values.compareAndSet(oldv, newv)){ return; } } }}

性能比较:锁与原子变量

  • 基于ReentrantLock的随机数生成器:
/** * 基于ReentrantLock实现的随机数生成器 */public class ReentrantLockPreudoRandom extends PseudoRandom{	private final Lock lock = new ReentrantLock(false);	private int seed;		public ReentrantLockPreudoRandom(int seed){		this.seed = seed;	}		public int nextInt(int n){		lock.lock();		try{			int  s = seed;			seed = calculateNext(s);			int remainder = s % n;			return remainder > 0 ? remainder : remainder + n;		} finally{			lock.unlock();		}	}       ...}
  • 基于AtomicInteger的随机数生成器
/** * 基于AtomicInteger实现的随机数生成器 */public class AtomicPseudoRandom extends PseudoRandom{	private AtomicInteger seed;		public AtomicPseudoRandom(int seed){		this.seed = new AtomicInteger(seed);	}		public int nextInt(int n){		while (true){			int s = seed.get();			int nextSeed = calculateNext(s);			if (seed.compareAndSet(s, nextSeed)){				int remainder = s % n;				return remainder > 0 ? remainder: remainder + n;			}		}	}       ...}

非阻塞算法:

  • 一个线程的失败或挂起不会导致其他线程也失败或挂起,那么这种算法就是非阻塞算法

非阻塞栈实现:

/** * 使用Treiber算法构造的非阻塞栈 */public class ConcurrentStack
{ private AtomicReference
> top = new AtomicReference
>(); public void push(E item){ Node
newHead = new Node
(item); Node
oldHead; do{ oldHead = top.get(); newHead.next = oldHead; } while (!top.compareAndSet(oldHead, newHead)); } public E pop(){ Node
oldHead; Node
newHead; do { oldHead = top.get(); if (oldHead == null) return null; newHead = oldHead.next; } while (!top.compareAndSet(oldHead, newHead)); return oldHead.item; } private static class Node
{ public final E item; public Node
next; public Node(E item){ this.item = item; } }}

非阻塞链表的插入操作实现:

/** * 链表中非阻塞算法中的插入排序,来自Michael-Scott */public class LinkedQueue
{ private static class Node
{ final E item; final AtomicReference
> next; public Node(E item, Node
next){ this.item = item; this.next = new AtomicReference<>(next); } } private final Node
dummy = new Node
(null, null); private final AtomicReference
> head = new AtomicReference<>(dummy); private final AtomicReference
> tail = new AtomicReference<>(dummy); public boolean put(E item){ Node
newNode = new Node
(item, null); while (true){ Node
curTail = tail.get(); Node
tailNext = curTail.next.get(); if (curTail == tail.get()){ //尾部还未修改 if (tailNext != null){ // 队列处于中间状态(即新节点已经接上,尾节点还未更新),推进尾节点 tail.compareAndSet(curTail, tailNext); } else{ // 处于稳定状态, 尝试插入新节点 if (curTail.next.compareAndSet(null, newNode)){ // 插入成功后,推进尾节点 tail.compareAndSet(curTail, tailNext); return true; } } } } }}

原子的域更新器:

private static class Node
{ private final E item; private volatile Node
next; // volatile变量 public Node(E item){ this.item = item; } } // 基于CAS的更新器 private static AtomicReferenceFieldUpdater
nextUpdater = AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "next");

ABA问题:

  • ABA问题是一种一场现象:如果在算法中的节点可以被循环使用,那么在使用"比较并交换"时就可能出现这种问题。在某些算法中,V的值由A变为B, 再由B变为A,仍然被认为发生了变化,并需要重新执行算法中的某些步骤。
  • 可通过AtomicStampedReferenceAtomicMarkableReference变量来避免ABA问题。

不吝指正。

转载于:https://my.oschina.net/indestiny/blog/263768

你可能感兴趣的文章
ad&mysqlslap压力测试
查看>>
saltstack 快速入门
查看>>
图像处理之理解卷积
查看>>
五种基于RGB色彩空间统计的皮肤检测算法
查看>>
RAID0、RAID1、RAID0+1、RAID5原理介绍
查看>>
Win8 Metro App里玩XNA:ContentPipeline内容管线问题
查看>>
java学习 - 数组-选择排序
查看>>
Centos Nginx+PHP Install 史上最完美
查看>>
PC基础概念
查看>>
一些作业脚本
查看>>
如何设置自动关机
查看>>
nginx常用配置
查看>>
SCCM 2012 简体中文正式版 部署文档 01 环境说明
查看>>
我的友情链接
查看>>
oracle asm 错误集
查看>>
然后再带动更多的C++人逼起来
查看>>
Linux运维 第三阶段 (一) 网络配置及openssl加密
查看>>
如何启用或禁用错过的呼叫skype for business通知
查看>>
tyvj——P3524 最大半连通子图
查看>>
Linux常用命令汇总--cat
查看>>