自定义代码实现简单的CAS逻辑

    技术2022-07-21  87

    场景:

    创建20个线程,每一个线程循环给成员变量加一,循环次数为100次,理论结果线程结束后结果为2000.

    下面先给出基本代码:

    package com.springcloud.server.springserver.thread; public class CasDemo { private static int count = 0; public static void main(String[] args) { for (int i = 0; i < 20; i++) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } //每个线程让count自增1000次 for (int i = 0; i < 1000; i++) { increase(); } } }).start(); } try{ Thread.sleep(200); }catch (Exception e){ e.printStackTrace(); } System.out.println(count); } static void increase(){ count++; } }

    输出结果并不是2000,而是比2000小,这是因为count++这个操作,并非原子操作.

    有两种方法可以解决这个问题:

    使用悲观锁,直接在increase()方法上加上synchronized关键字,使得每一次只有一个线程进入这个自增的方法,不过这个办法不是最优的方法.使用乐观锁,采用CAS的方法

    下面给出使用CAS原理改造的代码:

    package com.springcloud.server.springserver.thread; public class CasDemo { private static volatile int count = 0; public static void main(String[] args) { for (int i = 0; i < 20; i++) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } //每个线程让count自增1000次 for (int i = 0; i < 1000; i++) { increase(); } } }).start(); } try{ Thread.sleep(200); }catch (Exception e){ e.printStackTrace(); } System.out.println(count); } static void increase(){ int expect; while (!compareAndSwap(expect = getCount(),expect+1)){ } } public static synchronized boolean compareAndSwap(int expected ,int newCount){ if (getCount() == expected){ count = newCount; return true; } return false; } public static int getCount(){ return count; } }

     我们在return fasle打断点,查看这个cas的猫腻:

    因为count使用了volatile修饰,所以只要你取这个count值,它(从主存取)一定是正确的,那么为什么这个值是19,而不是2呢?

    其实上35行的代码有三个操作:

    1.获取count值

    2.count值加一

    3.进入compareAndSwap()方法,进行判断(加锁)

    我们的锁只能保证第三步的操作是原子性的,1,2,3这三个步骤并非原子性,假设某一个线程已经走了第一步和第二步,而且expected为2,newCount为3,但是现在count的值已经被其他线程改成了19,那么它就会进入false这个分支.然后它就会再次进入while循环,重新获取count的值,直到返回true,跳出循环,得到正确的值.

    这样,简易版的CAS代码就完成了.

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    Processed: 0.010, SQL: 9