多线程04--线程的原子性、CAS

多线程04--线程的原子性、CAS,第1张

上一篇:多线程04--线程的有序性_fengxianaa的博客-CSDN博客

        程序的原子性指整个程序中的所有 *** 作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节,有着“同生共死”的感觉。对线程而言,一个 *** 作一旦开始,就不会被其他线程所干扰。

java中必须借助于synchronized、Lock、锁等,来保证整块代码的原子性

/**
 * volatile不能保证原子性
 */
class Demo2 implements Runnable {
    volatile int a = 0;

    @Override
    public void run() {
        for(int i =0;i<10000;i++){
            a++;
        }
    }
}
public class T04_Volatile {

    public static void main(String[] args) throws InterruptedException {
        Demo2 d = new Demo2();
        Thread t1 = new Thread(d);
        Thread t2 = new Thread(d);
        t1.start();t2.start();
        t1.join();t2.join();
        System.out.println(d.a);
    }
}
3. CAS
/**
 * CAS: 全称 Compare And Set
 * 过程:1. 先获取最新值a,
 *      2. 进行CAS *** 作, *** 作时先判断值是否还是a,如果是就设置新值,如果不是,一直重复整个过程,称之为:自旋,
 *         整个cas *** 作是CPU原语级,不可被打断,所以是原子性的
 *
 *
 * 缺点:如果有大量的线程同时并发修改一个变量,那么可能会有很多线程会不停的自旋,这样性能就不好
 */
class Demo implements Runnable {
    AtomicInteger a = new AtomicInteger(0);

    @Override
    public void run() {
        for(int i =0;i<10000;i++){
            a.incrementAndGet();
        }
    }
}
public class T01_Cas {

    public static void main(String[] args) throws InterruptedException {
        Demo d = new Demo();
        Thread t1 = new Thread(d);
        Thread t2 = new Thread(d);
        t1.start();t2.start();
        t1.join();t2.join();
        System.out.println(d.a);
    }
}

流程图

 

LongAdder

/**
 * LongAdder,使用分段CAS以及自动分段迁移,提高并发性,最后计算出各个段的和值
 * 底层原理:
 *      1. 首先有一个base值,刚开始多线程来不停的累加数值,都是对base进行累加的,这时候跟AtomicInteger没有区别
 *      2. 如果线程数量过多,就会开始施行分段机制,内部会搞一个Cell数组,每个元素是一个数值分段。
 *         这时,让大量的线程分别去对不同Cell内部的value值进行CAS累加 *** 作,这样就把CAS计算压力分散到了Cell数组的各个元素,提高效率
 *
 *      3. 内部实现了自动分段迁移的机制,也就是如果某个Cell的value执行CAS失败了,那么就会自动去找另外一个Cell分段内的value值进行CAS *** 作,
 *         这样也解决了线程空旋转、自旋不停等待执行CAS *** 作的问题
 *
 *      4. 最后,如果你要从LongAdder中获取当前累加的总值,就会把base值和所有Cell分段数值加起来返回给你
 */
class Demo2 implements Runnable {
    LongAdder a = new LongAdder();

    @Override
    public void run() {
        for(int i =0;i<10000;i++){
            a.increment();
        }
    }
}

public class T02_LongAdder{

    public static void main(String[] args) throws InterruptedException {
        Demo2 d = new Demo2();
        Thread t1 = new Thread(d);
        Thread t2 = new Thread(d);
        t1.start();t2.start();
        t1.join();t2.join();
        System.out.println(d.a);
    }
}

流程图

ABA问题:

int a =1;

1. 线程1 获取a的最新值后,准备CAS *** 作,但这时线程暂停

2. 线程2 修改a的值为2

3. 线程3 又修改a的值为1

4. 线程1 继续运行,修改a的值。

这种情况下,线程1 虽然可以修改成功,但是这个a已经不是最初的a了,中间经历了一些变化,可能含义不同了,如果修改成功,可能导致一些问题,这就是ABA问题。

解决:加时间戳或版本号

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/739754.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-28
下一篇 2022-04-28

发表评论

登录后才能评论

评论列表(0条)

保存