Java Concurrency — 原子变量类
2015-10-25 14:05
288 查看
《Java并发编程实践》第15章 原子变量与非阻塞同步机制 读书笔记
显示锁
volatile
原子变量
都提供了可见性保证。
volatile变量不能用于构建原子化的复合操作,例如i++。
加锁的缺点。当一个线程正在等待锁时,它不能做任何事情。如果另一个线程在持有锁的情况下发生延迟(原因包括页错误、调度延迟等),那么其他所有需要该锁的线程都不能前进了。
原子变量类共12个,分4组
计数器
域更新器(field updater)
数组
复合变量
Atomic类的实现依赖于冲突监测,从而能判定更新过程中是否存在来自于其他成员的干涉,在冲突发生的情况下,操作失败,并会重试(也可能不重试)。
现代的处理器提供了原子化的读-改-写指令,如比较并交换(compare-and-swap)。CAS有3个操作数,内存位置V,旧的预期值A,新值B。当且仅当V等于旧的预期值A时,CAS用新值B原子化地更新V的值,否则什么都不会做。在任何一种情况下,都会返回V的真实值。
若处理器不支持这样的指令,JVM会使用自旋锁。
利用AtomicInteger实现i++复合操作。
相关Java源码
incrementAndGet方法体内是一个for循环,表示如果冲突发生,就不断重试,直到compareAndSet方法返回true。
compareAndSet方法调用的是Unsafe类的compareAndSwapInt方法,该方法是一个native方法。
该native方法的最终实现源码路径为openjdk\hotspot\src\os_cpu\windows_x86\vm\atomic_windows_x86.inline.hpp
如源代码所示,如果是多处理器机器,LOCK_IF_MP(mp)就会为cmpxchg指令加上lock前缀。原因是有可能多个处理器同时从各自的缓存中读取共享变量,分别进行操作,然后分别写入系统内存当中。这时就需要加锁,其中一种方式就是总线锁。
AtomicStampedReference更新对象引用的整数对
AtomicMarkableReference更新对象引用的布尔对
Java中的对共享变量访问的同步机制
synchronized内部锁显示锁
volatile
原子变量
volatile变量 VS 锁
volatile变量更轻量级,因为它们不会引起上下文的切换和线程的调度。对于锁,当频繁地发生锁的竞争时,上下文切换和调度开销可能远大于工作开销。都提供了可见性保证。
volatile变量不能用于构建原子化的复合操作,例如i++。
加锁的缺点。当一个线程正在等待锁时,它不能做任何事情。如果另一个线程在持有锁的情况下发生延迟(原因包括页错误、调度延迟等),那么其他所有需要该锁的线程都不能前进了。
原子变量类(Atomic Variable Classes)
于是,就需要类似于volatile变量的机制,并且还要支持原子化更新的技术。原子变量类就满足了这样的需求。原子变量类共12个,分4组
计数器
AtomicInteger AtomicLong AtomicBoolean AtomicReference
域更新器(field updater)
AtomicIntegerFieldUpdater AtomicLongFieldUpdater AtomicReferenceFieldUpdater
数组
AtomicIntegerArray AtomicLongArray AtomicReferenceArray
复合变量
AtomicMarkableReference AtomicStampedReference
Atomic类的实现依赖于冲突监测,从而能判定更新过程中是否存在来自于其他成员的干涉,在冲突发生的情况下,操作失败,并会重试(也可能不重试)。
现代的处理器提供了原子化的读-改-写指令,如比较并交换(compare-and-swap)。CAS有3个操作数,内存位置V,旧的预期值A,新值B。当且仅当V等于旧的预期值A时,CAS用新值B原子化地更新V的值,否则什么都不会做。在任何一种情况下,都会返回V的真实值。
若处理器不支持这样的指令,JVM会使用自旋锁。
利用AtomicInteger实现i++复合操作。
AtomicInteger i = new AtomicInteger(0); i.incrementAndGet();
相关Java源码
package java.util.concurrent.atomic; import sun.misc.Unsafe; public class AtomicInteger extends Number implements java.io.Serializable { public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } } public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } }
incrementAndGet方法体内是一个for循环,表示如果冲突发生,就不断重试,直到compareAndSet方法返回true。
compareAndSet方法调用的是Unsafe类的compareAndSwapInt方法,该方法是一个native方法。
public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);
该native方法的最终实现源码路径为openjdk\hotspot\src\os_cpu\windows_x86\vm\atomic_windows_x86.inline.hpp
// Adding a lock prefix to an instruction on MP machine // VC++ doesn't like the lock prefix to be on a single line // so we can't insert a label after the lock prefix. // By emitting a lock prefix, we can define a label after it. #define LOCK_IF_MP(mp) __asm cmp mp, 0 \ __asm je L0 \ __asm _emit 0xF0 \ __asm L0: inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compare_value) { // alternative for InterlockedCompareExchange int mp = os::is_MP(); __asm { mov edx, dest mov ecx, exchange_value mov eax, compare_value LOCK_IF_MP(mp) cmpxchg dword ptr [edx], ecx } }
如源代码所示,如果是多处理器机器,LOCK_IF_MP(mp)就会为cmpxchg指令加上lock前缀。原因是有可能多个处理器同时从各自的缓存中读取共享变量,分别进行操作,然后分别写入系统内存当中。这时就需要加锁,其中一种方式就是总线锁。
ABA问题
CAS只检测”V的值是否仍为A“,然而可能V的变化过程是A -> B -> A,这就是ABA问题。要知道“V的值在我上次观察后是否发生变化”,一种解决方案是,更新一对值,包括引用和版本号。A改为B,又改回A,版本号不同。AtomicStampedReference或AtomicMarkableReference提供了一对变量原子化的条件更新。AtomicStampedReference更新对象引用的整数对
AtomicMarkableReference更新对象引用的布尔对
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树
- [原创]java局域网聊天系统