java并发编程实践学习(15)原子变量与非阻塞同步机制
2017-02-22 19:55
686 查看
近来很多关于并发算法的研究机构都聚焦在非阻塞算法上,这种算法使用低层原子化的机器指令取代锁,比如比较并交换
加锁还有其他缺点。当一个线程正在等待锁时,它不能做任何其他事情。如果一个线程在持有锁的情况下发生了延迟,如果阻塞的线程是优先级很高的线程,持有锁的线程是优先级较低的线程,那么会造成优先级倒置的风险,即使高优先级占先,他仍然需要等待锁被释放,这导致他的优先级会降至与优先级较低的线程相同的水平。
当多个线程试图调用CAS同时更新想的的变量时,其中一个会胜出,并更新变量的值,而其它都会失败,失败的线程不会被挂起;
模拟CAS操作
原子变量类共有12个,分成四组:计量器、域更新器、数组以及复合变量。最常用的原子变量是计量器:AtomicInteger、AtomicLong、AtomicBoolean以及AtomicReference。它们都支持CAS,AtomicInteger和AtomicLong还支持算数运算,对于short和byte把它们的值强制转化为int;对于浮点数,使用floatToIntBits或doubleToLongBits。
原子化数组类(只有Integer、Long和Reference版本可用)它的元素可以被原子化的更新。原子数组类为数组元素提供了volatile的访问语义,这是普通数组元素没有的特性。
栈是最简单的链式数据结构:每个元素仅仅引用唯一的元素,并且每个元素值被一个元素引用。
使用Treiber算法的非阻塞栈
一、锁的劣势
当频繁发生锁的竞争时,调度与真正用于工作的开销时间的比会变得很可观。volatile变量与锁相比是更清凉的同步机制,因为他们不会引起上下文的切换和线程调度。加锁还有其他缺点。当一个线程正在等待锁时,它不能做任何其他事情。如果一个线程在持有锁的情况下发生了延迟,如果阻塞的线程是优先级很高的线程,持有锁的线程是优先级较低的线程,那么会造成优先级倒置的风险,即使高优先级占先,他仍然需要等待锁被释放,这导致他的优先级会降至与优先级较低的线程相同的水平。
二、硬件对并发的支持
针对多处理器系统设计的处理器提供了特殊的指令,用来管理并发访问的共享数据。早期处理器具有原子化的测试并设置,获取并增加,以及交换指令,现代的处理器都具有一些形式的原子化的读-改-写指令,比如比较并交换和加载链接/存储条件。操作系统和JVM使用这些指令来实现锁和并发的数据结构。1、比较并交换
包括IA32和、Sparc在内的大多数处理器使用的架构方案都实现了比较并交换指令(CAS)。CAS有三个操作数-内存位置V、旧的预期值A和新值B、当且仅当V符合旧预期值A时,CAS用新值B原子化的更新V的值;否则它什么都不做。当多个线程试图调用CAS同时更新想的的变量时,其中一个会胜出,并更新变量的值,而其它都会失败,失败的线程不会被挂起;
模拟CAS操作
@ThreadSafe public class SimulatedCAS{ @GuardedBy("this") private int value; public synchronized int get(){ return value; } public synchronized int compareAndSwap(int expectedValue,int new Value){ int = oldValue = value; if(oldValue == value){ value = new Value; } return oldValue; } public synchronized boolean compareAndSet(int expectedValue, int newValue){ return (expectedValue == compareAndSwap(expectedValue,newValue)); } }
2、JVM对CAS的支持
在Java5.0中引入了CAS的底层支持,将int、long和对象的引用暴露给CAS操作,并且JVM把它们编译为底层硬件提供的最有效的方法。在支持CVS的平台上,运行时把它们编排成恰当的机器指令,在CAS不可用的情况下,JVM才会使用自旋锁,这些底层的JVM支持用于那些具有原子化变量的类(java.util.concurrent.atomic中的AtomicXXX),而且这些原子变量类还用于直接或间接的实现java.util.concuttent中的大部分实现。三、原子变量类
原子变量类,提供了广义的volatile变量,以及支持原子的、条件的读-写改操作原子变量类共有12个,分成四组:计量器、域更新器、数组以及复合变量。最常用的原子变量是计量器:AtomicInteger、AtomicLong、AtomicBoolean以及AtomicReference。它们都支持CAS,AtomicInteger和AtomicLong还支持算数运算,对于short和byte把它们的值强制转化为int;对于浮点数,使用floatToIntBits或doubleToLongBits。
原子化数组类(只有Integer、Long和Reference版本可用)它的元素可以被原子化的更新。原子数组类为数组元素提供了volatile的访问语义,这是普通数组元素没有的特性。
4.非阻塞算法
一个线程的失败或挂起不影响其他线程的失败或挂起,这样的算法被称为为阻塞算法;如果算法的每一步中都有一些线程能够继续执行,这样的算法被称为锁自由算法。在线程间使用CAS进行协调,这样的算法如果能构建正确的话,它既是非阻塞的,又是锁自由的。1、非阻塞栈
在链式容器类中,有时你可以 不必在进行转化了而把它变为对单独链接的修改,你可以使用一个AtomicReference来表达每一个必须被原子的链接。栈是最简单的链式数据结构:每个元素仅仅引用唯一的元素,并且每个元素值被一个元素引用。
使用Treiber算法的非阻塞栈
public class ConcurrentStack<E> { AtomicReference<Node<E>> head = new AtomicReference<Node<E>>(); public void push(E item) { Node<E> newHead = new Node<E>(item); Node<E> oldHead; do { oldHead = head.get(); newHead.next = oldHead; } while (!head.compareAndSet(oldHead, newHead)); } public E pop() { Node<E> oldHead; Node<E> newHead; do { oldHead = head.get(); if (oldHead == null) return null; newHead = oldHead.next; } while (!head.compareAndSet(oldHead,newHead)); return oldHead.item; } static class Node<E> { final E item; Node<E> next; public Node(E item) { this.item = item; } } }
相关文章推荐
- 并发编程 21—— 原子变量和非阻塞同步机制
- 原子变量和非阻塞同步机制
- 多线程并发编程之原子变量与非阻塞同步机制
- Java并发编程之原子变量与非阻塞同步机制
- 原子变量与非阻塞同步机制
- Java并发:原子变量和非阻塞同步机制
- Java并发读书学习笔记(十一)——原子变量与非阻塞同步机制
- 并发编程实战学习笔记(十一)-原子变量与非阻塞同步机制
- 尝试Java加锁新思路:原子变量和非阻塞同步算法
- IO的学习笔记 - 同步,异步,阻塞,非阻塞
- 进程同步机制的优缺点比较,......同步机制包括 “原子操作 信号量机制 自旋锁 管程,会合,分布式系统”等
- IO的学习笔记 - 同步,异步,阻塞,非阻塞
- 内核部件之同步机制之原子操作
- linux互斥与同步 之 原子变量和位操作
- IO学习笔记:阻塞 非阻塞 同步 异步
- 【进程线程与同步】5.4 System.Threading.Interlocked 为多个线程共享的变量提供原子操作
- java并发编程4:原子变量与非阻塞算法
- 内核的同步机制(原子锁)
- boost c++ lib on linux(4) - thread同步条件变量学习——生产者消费者队列
- 内核同步机制——原子操作