一、锁的劣势

锁定后假设未释放。再次请求锁时会造成堵塞。多线程调度通常遇到堵塞会进行上下文切换,造成很多其它的开销。

在挂起与恢复线程等过程中存在着非常大的开销,而且通常存在着较长时间的中断。
锁可能导致优先级反转,即使较高优先级的线程能够抢先运行,但仍然须要等待锁被释放,从而导致它的优先级会降至低优先级线程的级别。

二、硬件对并发的支持

处理器填写了一些特殊指令,比如:比較并交换、关联载入/条件存储。



1 比較并交换
CAS的含义是:“我觉得V的值应该为A。假设是。那么将V的值更新为B,否则不须要改动告诉V的值实际为多少”。

CAS是一项乐观锁技术。


模拟CAS操作样例:
@ ThreadSafe
public class SimulatedCAS {
@ GuardeBy( "this") 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));
}
}

2 非堵塞的计数器
基于CAS实现的非堵塞计数器
@ ThreadSafe
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的主要缺点是:它将使调度者处理竞争问题(通过重试、回退、放弃),而在使用锁中能自己主动处理竞争问题(线程在获得锁之前将一直堵塞)。


3 JVM对CAS的支持

java.util.concurrent.atomic 类的小工具包,支持在单个变量上解除锁的线程安全编程。

AtomicBoolean 能够用原子方式更新的 boolean 值。
AtomicInteger 能够用原子方式更新的 int 值。
AtomicIntegerArray 能够用原子方式更新其元素的 int 数组。
AtomicIntegerFieldUpdater<T> 基于反射的有用工具,能够对指定类的指定 volatile int 字段进行原子更新。
AtomicLong 能够用原子方式更新的 long 值。 AtomicLongArray 能够用原子方式更新其元素的 long 数组。 AtomicLongFieldUpdater<T> 基于反射的有用工具。能够对指定类的指定 volatile long 字段进行原子更新。
AtomicMarkableReference<V> AtomicMarkableReference 维护带有标记位的对象引用。能够原子方式对其进行更新。
AtomicReference<V> 能够用原子方式更新的对象引用。 AtomicReferenceArray<E> 能够用原子方式更新其元素的对象引用数组。
AtomicReferenceFieldUpdater<T,V> 基于反射的有用工具。能够对指定类的指定 volatile 字段进行原子更新。
AtomicStampedReference<V> AtomicStampedReference 维护带有整数“标志”的对象引用,能够用原子方式对其进行更新。

三、原子变量类

1 原子变量是一种“更好的volatile”

通过CAS来维持包括多个变量的不变性条件样例:
import java.util.concurrent.atomic.AtomicReference;

public class CasNumberRange {
private static class IntPair{
final int lower ; // 不变性条件: lower <= upper
final int upper ; public IntPair( int lower, int upper) {
this .lower = lower;
this .upper = upper;
}
} private final AtomicReference<IntPair> values =
new AtomicReference<IntPair>( new IntPair(0, 0)); 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( "Cant't set lower to " + i + " > upper");
}
IntPair newv = new IntPair(i, oldv.upper );
if (values .compareAndSet(oldv, newv)){
return ;
}
}
}
// 对setUpper採用相似的方法
}



2 性能比較:锁与原子变量

使用ReentrantLock、AtomicInteger、ThreadLocal比較,通常情况下效率排序是ThreadLocal > AtomicInteger > ReentrantLock。

四、非堵塞算法

1 非堵塞的栈

import java.util.concurrent.atomic.AtomicReference;

public class ConcurrentStack<E> {
private AtomicReference<Node<E>> top = new AtomicReference<ConcurrentStack.Node<E>>(); public void push(E item){
Node<E> newHead = new Node<E>(item);
Node<E> oldHead; do {
oldHead = top .get();
newHead. next = oldHead;
} while (!top .compareAndSet(oldHead, newHead));
} public E pop(){
Node<E> oldHead;
Node<E> 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<E>{
public final E item;
public Node<E> next ; public Node(E item){
this .item = item;
}
}
}



2 非堵塞的链表
CAS基本使用模式:在更新某个值时存在不确定性,以及在更新失败时又一次尝试。
import java.util.concurrent.atomic.AtomicReference;

@ ThreadSafe
public class LinkedQueue<E> {
private static class Node<E>{
final E item;
final AtomicReference<Node<E>> next; public Node(E item, Node<E> next){
this .item = item;
this .next = new AtomicReference<Node<E>>(next);
}
} private final Node<E> dummy = new Node<E>( null , null );
private final AtomicReference<Node<E>> head =
new AtomicReference<Node<E>>(dummy);
private final AtomicReference<Node<E>> tail =
new AtomicReference<Node<E>>(dummy); public boolean put(E item){
Node<E> newNode = new Node<E>(item, null);
while (true ){
Node<E> curTail = tail.get();
Node<E> 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 ;
}
}
}
}
}
}



3 原子的域更新器
原子的域更新器类表示有volatile域的一种基于反射的“视图”,从而可以在已有的volatile域上使用CAS
private static class Node<E>{
private final E item;
private volatile Node<E> next; public Node(E item){
this.item = item;
}
} private static AtomicReferenceFieldUpdater<Node, Node> nextUpdater
= AtomicReferenceFieldUpdater.newUpdater(Node.class , Node.class , "next" );



4 ABA问题
处理V的值首先由A变成B。再由B变成A的问题。


《Java并发编程实战》第十五章 原子变量与非堵塞同步机制 读书笔记的更多相关文章

  1. java并发编程实战:第十五章----原子变量与非阻塞机制

    非阻塞算法:使用底层的原子机器指令(例如比较并交换指令)代替锁来确保数据在并发访问中的一致性 应用于在操作系统和JVM中实现线程 / 进程调度机制.垃圾回收机制以及锁和其他并发数据结构 可伸缩性和活跃 ...

  2. 《Java并发编程实战》第五章 同步容器类 读书笔记

    一.同步容器类 1. 同步容器类的问题 线程容器类都是线程安全的.可是当在其上进行符合操作则须要而外加锁保护其安全性. 常见符合操作包括: . 迭代 . 跳转(依据指定顺序找到当前元素的下一个元素) ...

  3. Java并发编程实战 第15章 原子变量和非阻塞同步机制

    非阻塞的同步机制 简单的说,那就是又要实现同步,又不使用锁. 与基于锁的方案相比,非阻塞算法的实现要麻烦的多,但是它的可伸缩性和活跃性上拥有巨大的优势. 实现非阻塞算法的常见方法就是使用volatil ...

  4. java并发编程实战:第五章----基础构建模块

    委托是创建线程安全类的一个最有效的策略:只需让现有的线程安全类管理所有的状态即可. 一.同步容器类 1.同步容器类的问题 同步容器类都是线程安全的,容器本身内置的复合操作能够保证原子性,但是当在其上进 ...

  5. 【java并发编程实战】第五章:基础构建模块

    1.同步容器类 它们是线程安全的 1.1 vector和hashtable. 和Collections.synchronizeXxx()一样.实现方式就是在每个方法里面加入synchronize代码块 ...

  6. 《Java并发编程实战》第三章 对象的共享 读书笔记

    一.可见性 什么是可见性? Java线程安全须要防止某个线程正在使用对象状态而还有一个线程在同一时候改动该状态,并且须要确保当一个线程改动了对象的状态后,其它线程能够看到发生的状态变化. 后者就是可见 ...

  7. 《Java并发编程实战》第十一章 性能与可伸缩性 读书笔记

    造成开销的操作包含: 1. 线程之间的协调(比如:锁.触发信号以及内存同步等) 2. 添加�的上下文切换 3. 线程的创建和销毁 4. 线程的调度 一.对性能的思考 1 性能与可伸缩性 执行速度涉及下 ...

  8. 《Java并发编程实战》第六章 任务运行 读书笔记

    一. 在线程中运行任务 无限制创建线程的不足 .线程生命周期的开销很高 .资源消耗 .稳定性 二.Executor框架 Executor基于生产者-消费者模式.提交任务的操作相当于生产者.运行任务的线 ...

  9. 《Java并发编程实战》第七章 取消与关闭 读书笔记

        Java没有提供不论什么机制来安全地(抢占式方法)终止线程,尽管Thread.stop和suspend等方法提供了这种机制,可是因为存在着一些严重的缺陷,因此应该避免使用. 但它提供了中断In ...

随机推荐

  1. poj2001 Shortest Prefixes (trie)

    读入建立一棵字母树,并且每到一个节点就增加这个节点的覆盖数. 然后再重新扫一遍,一旦碰到某个覆盖数为1就是这个单词的最短前缀了. 不知为何下面的程序一直有bug……不知是读入的问题? type nod ...

  2. NOI2006最大获利

    终于搞明白了…… x到y有边意味着选x必须选y,所以才会有闭合子图中一个点的后继一定也在这个闭合子图中. 接下来按照s连正权,负权连t就ok了 type node=record go,next,c:l ...

  3. Linux 根文件系统制作

    1.创建根文件目录 mkdir rootfs(名字是随便取的) 2.创建子目录 cd rootfs mkdir bin dev etc lib proc sbin sys usr mnt tmp va ...

  4. windows官方多语言方案

    编写 Win32 多语言用户界面应用程序 Windows 2000 针对全球市场制定了新的增强支持标准,提供了许多国际化功能,例如完全支持 Unicode.预设支持数百种语言以及用于从右向左语言的镜像 ...

  5. ♫【jQuery插件】图片放大镜

    JQZoom

  6. 【转】Android AlertDialog 点击对话框外部区域不关闭的设置

    原文网址:http://blog.sina.com.cn/s/blog_8f1c79dd0101a63u.html 在Android开发中,常常需要调用对话框,但会遇到这样一种情况,在显示对话框的时候 ...

  7. 【转】报错:Program "sh" not found in PATH

    原文网址:http://www.cnblogs.com/SadNight/p/3406201.html (1) 报错:Program "sh" not found in PATH ...

  8. SharePoint 2010 自定义 字段 类型--------省市区联动

    转:http://www.cnblogs.com/sp007/p/3384310.html 最近有几个朋友问到了有关自定义字段类型的问题,为了让更多的人了解自定义字段类型的方法,特写一篇博客与大家分享 ...

  9. C# 有关文件路径的操作

    1. 由文件全路径,获取文件扩展名.文件名等信息 string fullPath = @"\WebSite1\Default.aspx"; string filename = Sy ...

  10. log4j和web.xml配置webAppRootKey 的问题

    在tomcat下部署两个或多个项目时,web.xml文件中最好定义webAppRootKey参数,如果不定义,将会缺省为“webapp.root”,如下: <!-- 应用路径 --> &l ...