java atomic超详细剖析 CAS-LongAddr -AtomicReference-AtomicReferenceFieldUpdater-AtomicStampReference

atomic 类是线程安全的类

线程安全性
在这里插入图片描述

体现在三点
在这里插入图片描述

提起原子性,不得不提起jdk 的atomic包

atomic包提高原子更新基本类型的工具类,jdk主要提供:
在这里插入图片描述

例子

package com.mmall.concurrency.annoations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 课程里用来标记【线程安全】的类或者写法
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface ThreadSafe {

    String value() default "";
}

package com.mmall.concurrency.annoations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 课程里用来标记【线程不安全】的类或者写法
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface NotThreadSafe {

    String value() default "";
}

在这里插入图片描述

package com.mmall.concurrency.atomic;

import com.mmall.concurrency.annoations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;

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

/**
 * @Author: wn
 * @Description: 线程不安全的类,反面案例
 * 重点在于理解CountDownLath 和 Semaphore 作用和用法
 * @Date: Created in 2019/1/11 11:45
 */
@Slf4j //日志
@ThreadSafe//自定义注解类
public class AtomicExample0 {

    // 请求总数
    public static int clientTotal = 5000;

    // 同时并发执行的线程数
    public static int threadTotal = 200;

    public static int count = 0;

    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool(); //创建线程池
        final Semaphore semaphore = new Semaphore(threadTotal);  //semaphore信号量,允许并发的数目
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);//闭锁,计数器,传入请求总数
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    //信号量,acquire()时表示,当前线程数是否允许被执行,如果达到了一定并发数,这个add()方法将被阻塞
                    semaphore.acquire();
                    add();
                    semaphore.release();//信号量,执行完后,要释放当前线程release()
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();//没执行完一个线程,计数器减一
            });
        }
        countDownLatch.await();//countDownLatch.await()方法保证当前计数器为0,也就是所有的线程已经执行完
        executorService.shutdown();//关闭线程池
        log.info("count:{}", count);//执行完所有线程后,打印count值
    }

    private static void add() {
        count++;
    }
}

运行结果
5000
4989
4983
4805
运行结果不定,不安全

例子:
在这里插入图片描述

package com.mmall.concurrency.atomic;

import com.mmall.concurrency.annoations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;

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

/**
 * @Author: wn
 * @Description: 用atomic 类实现原子性
 * @Date: Created in 2019/1/11 11:56
 */
@Slf4j
@ThreadSafe //自定义注解类
public class AtomicExample1 {

    // 请求总数
    public static int clientTotal = 5000;

    // 同时并发执行的线程数
    public static int threadTotal = 200;
    //atomic 类,
    public static AtomicInteger count = new AtomicInteger(0);

    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count.get());
    }

    private static void add() {
        count.incrementAndGet();//原子性操作,先累加再获取值,此处,这两个没有区别,只有在打印输出是有区别
        // count.getAndIncrement();//原子性操作,先获取值再累加
    }
}

运行结果
5000
5000
5000
运行结果一定,安全

重点讲解:
在这里插入图片描述

public static AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet();

在这里插入图片描述
在这里插入图片描述

重点是unsafe类的getAndAddInt()方法的实现,这里实现原理是cas原理

getAndAddInt(Object var1, long var2, int var4)
参数:
var1为当前对象,
var2,var4为加数,比如2+1,这里var2=2,var4=1
var5 表示从底层(主内存)获取的数据

一个do while() 循环,

this.compareAndSwapInt(var1, var2, var5, var5 + var4) 方法:
传入当前对象,var1,
var2 是当前对象的变量,(也就是线程工作空间中的变量)
var5 是从底层(主内存)获取的数据
var5 + var4 表示 底层数据加累加值
原理:如果var2 == var5 时,执行var5 + var4,
如果var2!=var5 时,则重新从底层读取数据var5 ,重新读取对象var1的变量var2 ,判断var2 == var5,然后在执行var5 + var4

比如:一个线程b调用getAndAddInt(object,2,1)
此时有一个线程a已经完成加一操作,也就是主内存中变量var1的值为3,var5 = 3
线程b调用compareAndSwapInt(var1, var2, var5, var5 + var4) 时,
var2 !=var5 即2!=3,
此时,都重新获取var2,var5,这时,var2也已经是3 ,这时,var2==var5 ,然后执行var5 + var4,

这就保证了atomic 类的原子性累加
,cas就是 compareAndSwapInt () 的缩写

atomicLong ,jdk1.8 新增了一个LongAddr ,与atomicLong 类似

在这里插入图片描述

例子:
在这里插入图片描述

package com.mmall.concurrency.atomic;

import com.mmall.concurrency.annoations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @Author: wn
 * @Description:  atomicLong 原子性
 * @Date: Created in 2019/1/11 12:16
 */
@Slf4j
@ThreadSafe
public class AtomicExample2 {

    // 请求总数
    public static int clientTotal = 5000;

    // 同时并发执行的线程数
    public static int threadTotal = 200;

    public static AtomicLong count = new AtomicLong(0);

    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count.get());
    }

    private static void add() {
        count.incrementAndGet();
        // count.getAndIncrement();
    }
}

运行结果
5000
5000
5000
运行结果一定,安全

在这里插入图片描述

package com.mmall.concurrency.atomic;

import com.mmall.concurrency.annoations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.LongAdder;

/**
 * @Author: wn
 * @Description: jdk1.8新增LongAdder
 * @Date: Created in 2019/1/11 12:18
 */
@Slf4j
@ThreadSafe
public class AtomicExample3 {

    // 请求总数
    public static int clientTotal = 5000;

    // 同时并发执行的线程数
    public static int threadTotal = 200;

    public static LongAdder count = new LongAdder();

    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count);
    }

    private static void add() {
        count.increment();
    }
}

运行结果
5000
5000
5000
运行结果一定,安全

atomicLongLongAddr 区别:

我们知道atomic 用的是acs 原理,这个是在一个死循环中,如果值不对,会一直尝试,如果并发量不大时,不怎么影响性能,但是当并发量非常大,竞争很激烈时,修改失败的概率就会很高, 就会影响性能,

CAS 死循环内不断尝试修改目标值直到修改成功,如果竞争不激烈的时候修改成功概率很高。竞争激烈的时候修改失败的概率很高,不断尝试就会影响性能。

long,double jvm 允许将64位的读操作或者写操作拆成2个32位的操作。

LongAdder的实现思想:热点数据分离。把内部核心数据value分离为数组,每个线程访问时通过hash等算法映射到其中一个数字计数,最终的计数结果是数字的求和累加,热点数据value就被分离成多个单元cell,每个cell单独维护自己的内部值,当前对象的值是由所有cell 累计合成,这样热点就进行了有效的分离,提高了并行度。这样一来LongAdder就将单点的执行压力分布到各个节点cell 中,在低并发时,可通对value的直接更新,可以保证和atomicLong 的性能基本一致,而在高并发时,通过分散提高性能。

LongAdder缺点:在统计时,如果有并发更新,会导致统计的数据有些误差

在实际使用时,可以优先使用LongAdder,而不是atomicLong ,当然在线程竞争很低的情况下,使用atomicLong 比较简单,直接,其他使用atomicLong情况,比如序列号生成的情况,准确的数值的情况,全局统一计数的情况还是应该选择atomic。。

AtomicBoolean

在这里插入图片描述

AtomicBoolean的
compareAndSet(boolean expect, boolean update)

实际中我们经常遇到,某些事情只执行一次,比如执行前是false,执行后是true,
如果直接调用
compareAndSet(false,true)//表示,如果是false时,更新为true;
就可以保证,我们要执行的代码只执行一次,甚至可以理解为当前只能有一个线程执行这个代码,在执行完后,这个标志就等于true,,,如果我们再标志为false,则这个代码又可以继续执行,这样这个代码可以同一时间只能一个线程执行,

例如
在这里插入图片描述
结果:
在这里插入图片描述

test 方法只执行了一次, 剩下的4999次,都没执行,保证了多线程下,代码只执行一次

AtomicReference 和AtomicIntegerFieldUpdater

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

执行结果:
在这里插入图片描述

AtomicStampReference :CAS的ABA 问题

CAS ABA 问题 :在CAS操作的时候其他线程将变量的值A改成了B又改回了A,本线程用期望值A比较时发现值没有变,于是CAS就将A值进行了交换操作,这值已经被其他线程改变过,与设计思想是不符合的。解决思路:每次变量更新的时候,把变量的版本号加一,A改成了B又改回了A的过程,就是A 版本1 B版本2 A版本3,, 从而解决ABA问题,

在这里插入图片描述

在这里插入图片描述

这里多了stamp 版本比较,其他的跟其他Atomic类的compareAndSet 方法类似

AtomicLongArray

AtomicLongArray维护的是个数组,我们可以选择性的更新某个索引的值,也是进行原子性操作的,相比AtomicLong,会多个索引值让我们更新,
举例:
getAndSet(int i ,long newValue) //表示传入一个索引,然后更新
compareAndSet(int i,long expect,long update)// 表示 传入一个索引,期望值为expect,更新为update

在这里插入图片描述

atomic 相关包很重要,要认真学习。

猜你喜欢

转载自blog.csdn.net/wangnanwlw/article/details/86292770