本文主要介绍jdk中的原子类、ABA问题以及多个变量之间的安全访问。
原子类中核心的一个语法就是CAS操作,而这个操作封装在Unsafe类中,典型的应用如下代码
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
这个是AtomicInteger类的部分代码,其中valueOffset的值很关键,cas操作都要依赖它的,看下cas操作的代码
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
它有四个参数,this表示实例,valueOffset可看做是value的地址,expect表示期望值,update表示更新值,要表达的意思就是valueOffset处的值等于expect就用update更新,这个操作是在硬件级别上实现的。
典型的应用如下
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
/**
* Atomically decrements by one the current value.
*
* @return the previous value
*/
public final int getAndDecrement() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return current;
}
}
这种应用很普遍,它是实现非阻塞算法的基础,但是它也有一个特用的问题,叫ABA问题,这个后面在说。
AtomicReference类也是一个常用类,这是针对所有类的一个原子操作的实现,原理和AtomicInteger类似;JDK中也提供了原子的域更新器,可以更新指定类的指定域名,但是原理还是如AtomicInteger类的cas一样,在ConcurrentLinkedQueue中有典型的应用。
现在来说说ABA问题,非阻塞算法的思路是先get一个变量的值,然后执行cas操作,如果失败重复以上的两步操作,成功就返回,问题就在get操作和cas操作之间,一个线程执行了get后,由于线程的交替,另一个线程改变了执行环境,第一个线程执行cas的时候会得到错误的结果。这种问题在链表的操作中比较典型,想具体了解请看这边文章
http://blog.hesey.net/2011/09/resolve-aba-by-atomicstampedreference.html ,(有图有描述)
在贴一段代码
public class ABATest {
private static AtomicInteger atomicInt = new AtomicInteger(100);
private static AtomicStampedReference<Integer> atomicStampedRef = new AtomicStampedReference<Integer>(100, 0);
public static void main(String[] args) throws InterruptedException {
// testAtomicInteger();
testAtomicStampedReference();
}
// 出现ABA问题
public static void testAtomicInteger(){
Thread intT1 = new Thread(new Runnable() {
public void run() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicInt.compareAndSet(100, 101);
atomicInt.compareAndSet(101, 100);
System.out.println("intT1 over");
}
});
// 在线程intT2获得oldValue,执行cas之前的时间段,线程intT1修改atomicInt两次,但intT2的cas操作还是成功执行了
Thread intT2 = new Thread(new Runnable() {
public void run() {
int oldValue=atomicInt.get();
System.out.println("intT2 start");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
}
boolean c3 = atomicInt.compareAndSet(oldValue, 101);
System.out.println(c3); // true
}
});
intT1.start();
intT2.start();
}
// 可以有效避免ABA问题
public static void testAtomicStampedReference(){
Thread refT1 = new Thread(new Runnable() {
public void run(){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
atomicStampedRef.compareAndSet(100, 101, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
atomicStampedRef.compareAndSet(101, 100, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
System.out.println("refT1 over");
}
});
// cas操作之前atomicStampedRef被修改,那么cas操作将失败
Thread refT2 = new Thread(new Runnable() {
public void run() {
int stamp = atomicStampedRef.getStamp();
System.out.println(stamp);
System.out.println("refT2 start");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
}
System.out.println(atomicStampedRef.getStamp());
boolean c3 = atomicStampedRef.compareAndSet(100, 101, stamp, stamp + 1);
System.out.println(c3); // false
}
});
refT1.start();
refT2.start();
}
}
这段代码中提到的ABA问题只是演示,它不会出现错误的结果。
在实现线程安全时,不可变类是一个很重要的概念,一个不可变的类的要求:对象的状态不可修改,对象的域都是final的,对象被正确的构造(不会发生this引用溢出)。多线程访问不可变对象一定是线程安全的,这也常应用于多个变量安全性保证上。如下代码保证两个变量的不变性约束
public class CasNumberRange {
// 将多个变量封装到一个对象中去
private class IntPair{
final int lower;
final int upper;
public IntPair(int lower, int upper){
this.lower=lower; // 不变约束:lower <= upper
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 oldValue=values.get();
if(i>oldValue.upper){
throw new IllegalArgumentException("Can't set lower to "+i+" > upper");
}
IntPair newValue=new IntPair(i,oldValue.upper);
if(values.compareAndSet(oldValue, newValue))
return;
}
}
public void setUpper(int i){
while(true){
IntPair oldValue=values.get();
if(i<oldValue.lower){
throw new IllegalArgumentException("Can't set upper to "+i+" < lower");
}
IntPair newValue=new IntPair(oldValue.lower,i);
if(values.compareAndSet(oldValue, newValue))
return;
}
}
}
在有些情况下,可以将多个变量封装在一个不可变类中实现线程安全。
分享到:
相关推荐
随着项目的复杂性增加,管理CSS代码变得越来越困难,这时引入原子类CSS的概念,可以显著提升CSS开发效率。本文将深入探讨原子类CSS(也称为原子设计或原子样式库)以及它如何改善JavaScript开发中的CSS相关工作流程...
Java多线程与并发处理是Java编程中的高级话题,涉及到JUC(java.util.concurrent)包中的原子类、CAS(Compare-And-Swap)机制、Unsafe类以及多线程并发的无锁方案和线程安全的实现方法。 CAS是一种无锁的同步机制...
Java 原子类是Java并发编程中非常重要的工具,它们提供了一种高效且线程安全的方式来更新变量,尤其在处理共享数据时。在JDK 7及之前,共有12个原子类,而在JDK 8中,又新增了4个。下面,我们将详细介绍这些原子类...
在Java编程语言中,原子类(Atomic Classes)是并发编程中的一个重要工具,它们提供了一种在多线程环境下实现高效、安全的变量操作方式。这些类位于`java.util.concurrent.atomic`包下,能够保证在不使用锁的情况下...
Java Atomic原子类的使用方法和原理 Java Atomic原子类是Java中的一种多线程安全机制,用于在多线程环境中保证变量的原子性操作。Atomic原子类的出现解决了多线程环境下变量操作的安全问题,使得开发者可以更方便地...
Java concurrency之AtomicReference原子类_动力节点Java学院整理,动力节点口口相传的Java黄埔军校
Java concurrency之AtomicLongFieldUpdater原子类_动力节点Java学院整理,动力节点口口相传的Java黄埔军校
Java concurrency之AtomicLong原子类_动力节点Java学院整理,动力节点口口相传的Java黄埔军校
一个模块化的Less基础类库,包含Reset、栅格系统、原子类、常用class和浏览器兼容等模块。 ##使用: 首先在你的less里引入 coffce.less,再根据你的需要,调用各个模块的init初始化模块 npm install coffce-less // ...
Java并发编程专题(十)----(JUC原子类)基本类型详解 本资源主要介绍了Java JUC原子类基本类型的相关知识点,包括AtomicInteger、AtomicLong、AtomicBoolean等基本类型的使用方法和原理。通过对原子操作和 일반操作...
Java 并发编程专题(十一)----(JUC 原子类)数组类型详解 Java 中的并发编程是指在多线程编程中,多个线程之间的协调和同步,以便实现高效、安全的并发执行。Java 提供了多种并发编程机制,其中 JUC(Java ...
Java `java.util.concurrent.atomic`包提供了以下主要的原子类: 1. `AtomicBoolean`, `AtomicInteger`, `AtomicLong`: 这些类分别提供了对布尔值、整型和长整型的原子操作。它们提供了`get()`, `set()`, `compare...
该文件主要讲解的是电子在库仑场中运动的氢原子模型,这是一个经典的量子力学问题,通常称为类氢原子。在库仑场中,电子受到来自正电核(如氢原子中的质子)的库仑力作用。以下是相关知识点的详细说明: 1. **...
Java并发编程领域中的一个重要工具是`java.util.concurrent.atomic`包,这个包中包含了一系列的原子类,它们提供了无锁线程安全的编程机制。原子类的设计目标是在多线程环境下,对变量的操作能够像单线程环境一样,...
`AtomicLongArray`是Java并发库提供的一个原子性操作长整型数组的类,它支持线程安全的增加、减少、比较并设置、获取和设置等操作,而无需使用锁或其他同步机制。这使得在高并发场景下,多个线程可以并发地修改数组...
引言JavaGuide :一份涵盖大部分Java程序员所需要掌握的核心知识。star:45159,替他宣传一下子这位大佬,总结的真好!我引用这位大佬的文章,因为
提出了实验中可行的利用部分纠缠态实现多原子类猫态的离物传送。在此方案中,离物传送多原子纠缠态的成功几率等于作为量子通道的部分纠缠态的较小叠加系数的模的平方的两倍,即,利用此类纠缠态作为量子通道传送实现...