版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_24871519/article/details/84439330
CAS 无锁算法及原子操作类实践
非原子性示例
先来看一段未实现原子性的自增统计代码:
private static volatile int x=0;
// 线程同时开始
private static CountDownLatch start=new CountDownLatch(1);
// 线程全部结束输出结果
private static CountDownLatch end=new CountDownLatch(20);
public static void main(String[] args) throws InterruptedException
{
for (int i = 0; i < 20; i++)
{
new Thread(new Runnable()
{
@Override
public void run()
{
try
{
start.await();
} catch (InterruptedException e)
{
e.printStackTrace();
}
for (int i = 0; i < 10000; i++)
{
x++;
}
end.countDown();
}
}).start();
}
start.countDown();
end.await();
System.out.println("compute over!!!");
System.out.println("result = "+x);
}
这段代码意思是:20个线程同时对变量x 自增10000次,期望输出结果是:result=200000。但我们多运行几次程序,总会发现结果有小于200000的情况。这是因为上诉代码x++ 并不是一个原子操作。
怎么来实现x++ 的原子性?加把锁吧,是可以解决的,不过每次自增都得加锁,性能会很受影响的。
CAS 无锁算法
突然想到并发包不是有原子操作类吗,AtomicInteger…Doug Lea 大神肯定不会用加锁的方式来实现吧!看了下其源码,得知这里使用了一种无锁的非阻塞算法,即CAS(compare and swap 比较然后交换)。
CAS 有三个操作数:内存值x, 旧值a, 新值b。主要思路是:当且仅当x==a,将x设置为b,否则放弃,然后开始不断重试上述操作, java 实现伪代码如下:
// 模拟内存值
private static volatile int x=0;
private static void cas(int a, int b){
do{
// 获取内存值
a=x;
}while(!retry(a, b));
}
private static boolean retry(int a, int b){
if(x==a){
x=b;
return true;
}
return false;
}
模仿AtomicInteger 类完成原子自增操作
// 自定义原子操作类MyInt, 提供原子加法功能
public class MyInt
{
private volatile int value;
private static long valueOffset;
private static Unsafe unsafe;
public MyInt(int v)
{
this.value=v;
unsafe=getUnsafe();
try
{
valueOffset=unsafe.objectFieldOffset(MyInt.class.getDeclaredField("value"));
} catch (Exception e)
{
e.printStackTrace();
}
}
public final int getAndAdd(int delt){
int res;
do{
res=unsafe.getIntVolatile(this, valueOffset);
}while (!unsafe.compareAndSwapInt(this, valueOffset, res, res+delt));
return res;
}
public final int get(){
return this.value;
}
private static Unsafe getUnsafe(){
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
return (Unsafe)field.get(null);
} catch (Exception e) {
}
return null;
}
}
// 以20线程10000次自增进行测试
public class TestMyInt
{
private static final int THREAD_COUNT=20;
private static final int COMPUTE_COUNT=10000;
private static MyInt value=new MyInt(0);
// 全部线程一起开始
private static final CountDownLatch threadStart=new CountDownLatch(1);
// 全部线程结束输出结果
private static final CountDownLatch threadEnd=new CountDownLatch(THREAD_COUNT);
public static void main(String[] args)
{
for (int i = 0; i < THREAD_COUNT; i++)
{
new Thread(new Runnable()
{
@Override
public void run()
{
try
{
threadStart.await();
} catch (InterruptedException e)
{
e.printStackTrace();
}
for (int i1 = 0; i1 < COMPUTE_COUNT; i1++)
{
value.getAndAdd(1);
}
threadEnd.countDown();
}
}).start();
}
threadStart.countDown();
try
{
threadEnd.await();
} catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("result = "+value.get());
}
}
多次测试结果都为:result=200000,满足原子性自增测试。