JAVA多线程基础:高效的无锁并行计算CAS算法

CAS算法为了解决什么问题?

我们为了在多线程下保持数据同步,不得不在不同的线程间进行锁竞争时,这样总不可避免相互等待,从而阻塞当前线程。这样导致的性能低下……

什么是CAS算法

如果我们让每个线程拥有各自独立的变量副本,当各个线程在并行计算时,无需相互等待对方的结果,最后将大家运行的结果拿出来对比对比“胜出”的就为最终的结果,所以这是一种基于比较交换的无锁并发控制方法。

算法优点

虽然无锁算法的设计和实现更为复杂的多,但是它有以下优点:

  • 由于其非阻塞性,它对死锁问题天生免疫,既无死锁问题
  • 线程间的相互影响也远远比基于锁的方式要小
  • 使用无锁的方式没有锁竞争带来的系统开销,也没有线程间频繁调度带来的开销,因此它要比有锁的方式拥有更优越的性能

CAS算法对比的思路

CAS算法拥有3个参数,它们分别是:

  • V:表示要更新的变量
  • E:表示预期值
  • N:表示新值
对比方案
  • 仅当更新的变量V预期值E相等的时候,将新值N赋值变量V,最后返回V

用代码的方式去理解:

if(V == E) V = N;
return V
  • 当多个线程同时使用CAS操作一个变量时,只有一个会胜出(能够更新V的),其余失败的可以再次尝试或者放弃
  • 备注:大部分现代的处理器已经支持原子化的CAS指令

代码

也许上面的原理讲解都很枯燥难懂,但是使用方式却特别简单,先看下面代码,再去理解原理可能会更好

我现在希望用3个线程去,将一个数acount,从0加到1百万,可以用下面代码实现

  • 我先用线程池开启3个线程,然后去调用**AtomicThread **类

AtomicInteger acount = new AtomicInteger(0); //这行代码,应该放在成员变量中

//下面代码放在main方法中
ExecutorService exe = Executors.newFixedThreadPool(3);//线程池
AtomicThread atomic = new AtomicThread();
for(int i = 0;i < 3;i++){
     exe.submit(atomic);
}
  • 下面重点在AtomicThread类中,关键代码就一行acount.incrementAndGet()
    public class AtomicThread implements Runnable{
        @Override
        public void run() {
            int v = acount.incrementAndGet();
            while (v < 1000000){ //实际可能会加到1000002,不过不重要
                v = acount.incrementAndGet(); // 关键代码
            }
            System.out.println("V=" + v);
        }
    }

原理说的那么复杂,其实调用也就这么一行代码,很简单吧!下面,我们再重点看看AtomicInteger类相关的API:

  • int get() : 取得当前值
  • set(int newValue) : 设置当前值
  • int getAndSet(int newValue) : 设置新值,并返回旧值
  • boolean compareAndSet(int expect, int u) : 这个就对应上面说的“对比”方案了

  • int getAndIncrement() : 当前值加1,返回旧值 类似于 i++
  • int getAndDecrement() : 当前值加1,返回旧值 类似于 i–
  • int getAndAdd(int delta) : 当前值加delta,返回旧值

  • int incrementAndGet() : 当前值加1,返回新值 类似 ++i
  • int decrementAndGet() : 当前值减1,返回新值 类似于 --i
  • int addAndGet(int delta) : 当前值加delta,返回新值
测试代码

下面是测试CAS算法传统加锁的方式的效率高低,不用说CAS算法肯定胜出(我的版本是JDK 1.8),不然我就不用写这篇博客啦,哈哈

import org.junit.jupiter.api.Test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

import static javafx.scene.input.KeyCode.L;

public class TestAtomic {
    private static final int MAX_THREADS = 3;
    private static final int TASK_COUNT = 3;
    private static final int TARGET_COUNT = 1000000;//循环100万次
    private AtomicInteger acount = new AtomicInteger(0);
    private int count = 10;
    protected synchronized int inc(){  return ++count;}

    protected synchronized int getCount(){return count;}

    //有锁的添加方式
    public class SyncThread implements Runnable{
        protected String name;
        protected long starttime;
        TestAtomic out;
        public SyncThread(TestAtomic out, long starttime){
            this.out = out;
            this.starttime = starttime;
        }


        @Override
        public void run() {
            int v = out.inc();
            while (v < TARGET_COUNT){
                v = out.inc();
            }
            long endtime = System.currentTimeMillis();
            System.out.println("SyncThread speed:" + (endtime - starttime) + "ms" + "V=" + v);
        }
    }

    //
    public class AtomicThread implements Runnable{
        protected long starttime;
        public AtomicThread(long starttime){
            this.starttime = starttime;
        }


        @Override
        public void run() {

            int v = acount.incrementAndGet();
            while (v < TARGET_COUNT){
                v = acount.incrementAndGet();
            }
            long endtime = System.currentTimeMillis();
            System.out.println("AtomicThread speed:" + (endtime - starttime) + "ms  " + "V=" + v);
        }
    }


    @Test
    public void testSync() throws InterruptedException{
        ExecutorService exe = Executors.newFixedThreadPool(MAX_THREADS);
        long starttime = System.currentTimeMillis();
        SyncThread sync = new SyncThread(this,starttime);
        for(int i = 0;i < TASK_COUNT;i++){
            exe.submit(sync);
        }
        Thread.sleep(10000);
    }

    @Test
    public void testAtomic() throws InterruptedException{
        ExecutorService exe = Executors.newFixedThreadPool(MAX_THREADS);
        long starttime = System.currentTimeMillis();
        AtomicThread atomic = new AtomicThread(starttime);
        for(int i = 0;i < TASK_COUNT;i++){
            exe.submit(atomic);
        }
        Thread.sleep(10000);
    }


//如果不会使用Junit测试,可以使用下面的main方法,效果一样,记得删除方法上面的@Test标签
//    public static void main(String[] args) throws InterruptedException {
//        TestAtomic testAtomic = new TestAtomic();
//        testAtomic.testSync();
//        testAtomic.testAtomic();
//    }
}
  • 如果不会使用Junit测试,可以使用最下面的main方法,效果一样,记得删除方法上面的@Test标签

我测试的效果差了5倍左右

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值