CAS 无锁算法及原子操作类实践

版权声明:本文为博主原创文章,未经博主允许不得转载。 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,满足原子性自增测试。

参考内容

猜你喜欢

转载自blog.csdn.net/qq_24871519/article/details/84439330
今日推荐