CAS与VarHandle

现在java中多使用cas来实现无锁化

定义

CAS(Compare and Swap),如果原始值和给定值相同,则修改为新值,否则失败

使用

借助java9推出的VarHandle实现cas操作

VarHandle通过以下方法获取

1
MethodHandles.lookup().findVarHandle(CASExample.class, "x", int.class)

三个参数依次代表:

  • CASExample.class 需要操作的变量所在的类
  • x 变量的名字,在此处就是变量在CASExample中的名字
  • int.class 变量的类型

调用如下:

1
Varhandle.compareAndSet(example, 0, 1)

第一个参数是CASEXample的实例,第二次参数是期望值,第三个参数就想要设置的新值

样例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class CASExample {

private volatile int x = 0;

public static void main(String[] args) {
try {
VarHandle xVarhandle = MethodHandles.lookup().findVarHandle(CASExample.class, "x", int.class);

CyclicBarrier barrier = new CyclicBarrier(2);

CASExample example = new CASExample();


for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
barrier.await();
while (true) {
if (xVarhandle.compareAndSet(example, 0, 1)) {
break;
}
}
System.out.println(Thread.currentThread().getName() + "设置完成" + example.x);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}


}
}).start();
}


} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}

}

}

输出一个1,同时cpu占有率开始上升,很明显是另一个线程死循环导致的


注意,我用的是openjdk11,jdk中存在两个Unsafe,分别是sun.misc.Unsafejdk.internal.misc.Unsafe,这里使用第二个

原子类

写一个原子类试一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

public class AtomicInt {

private volatile int value;
private VarHandle handle;

public AtomicInt() {
try {
value = 0;
handle = MethodHandles.lookup().findVarHandle(AtomicInt.class, "value", int.class);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}

}


public boolean add() {
while (true) {
int v = value;
if (handle.compareAndSet(this, v, v + 1)) {
return true;
}
}
}

public static void main(String[] args) {
int threadCount = 100;
int count = 1000;
CountDownLatch latch = new CountDownLatch(threadCount);

AtomicInt atomicInt = new AtomicInt();

for (int i = 0; i < threadCount; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < count; j++) {
atomicInt.add();
}
latch.countDown();
}
}).start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println(atomicInt.value);
}

}

结果是100000,正确

ABA问题

ABA是指数据由A->B->A的转换,CAS会误以为没有变化。JDK提供了AtomicStampedReferenceAtomicMarkableReference来解决。


CAS与VarHandle
http://blog.inkroom.cn/2021/06/13/1ST875F.html
作者
inkbox
发布于
2021年6月13日
许可协议