Java --- JUC之原子类

news2024/11/20 16:24:01

目录​​​​​​​

一、基本类型原子类

二、数组类型原子类

三、引用类型原子类

四、对象的属性修改类型原子类

五、原子操作增强类

5.1、高性能热点商品应用

5.2、LongAdder架构图

5.3、源码分析

一、基本类型原子类

public class AtomicTest1 {
    public static final int SIZE = 50;
    public static void main(String[] args) throws InterruptedException {
        MyAtomic myAtomic = new MyAtomic();
        CountDownLatch countDownLatch = new CountDownLatch(SIZE);
        for (int i = 1; i <= SIZE ; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <= 1000 ; j++) {
                        myAtomic.add();
                    }
                } finally {
                    countDownLatch.countDown();
                }
            },String.valueOf(i)).start();
        }
        //等待五十个线程全部计算完,获取结果
        countDownLatch.await();
        System.out.println("计算结果为:"+myAtomic.atomicInteger.get());
    }
}
class MyAtomic{
    AtomicInteger atomicInteger = new AtomicInteger();
    public void add(){
        atomicInteger.getAndIncrement();
    }
}

二、数组类型原子类

public class AtomicTest2 {
    public static void main(String[] args) {
        AtomicIntegerArray array = new AtomicIntegerArray(new int[]{1, 2, 3, 4});
        for (int i = 0; i <array.length() ; i++) {
            System.out.println(array.get(i));
        }
    }
}

三、引用类型原子类

public class AtomicTest3 {
    static AtomicMarkableReference markableReference = new AtomicMarkableReference(100,false);
    public static void main(String[] args) {
      new Thread(()->{
          boolean marked = markableReference.isMarked();
          System.out.println(Thread.currentThread().getName() + "\t" + marked);
          try {
              TimeUnit.MILLISECONDS.sleep(500);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
          markableReference.weakCompareAndSet(100, 101, marked, !marked);

      },"A").start();
        new Thread(()->{
            boolean marked = markableReference.isMarked();
            System.out.println(Thread.currentThread().getName() + "\t" + marked);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean b = markableReference.weakCompareAndSet(100, 102, marked, !marked);
            System.out.println(b+"\t"+Thread.currentThread().getName());
            System.out.println(Thread.currentThread().getName()+"\t"+markableReference.isMarked());
            System.out.println(Thread.currentThread().getName()+"\t"+markableReference.getReference());
        },"B").start();
    }
}

四、对象的属性修改类型原子类

1、使用目的:以一种线程安全的方式操作非线程安全对象内的某些字段

2、使用对象:①、更新对象属性必须使用public volatile修饰符。②、因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。

public class AtomicTest4 {
    public static void main(String[] args) throws InterruptedException {
        Bank bank = new Bank();
        CountDownLatch countDownLatch = new CountDownLatch(10);
        for (int i = 1; i <=10 ; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <=1000 ; j++) {
                        bank.add(bank);
                    }
                } finally {
                  countDownLatch.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch.await();
        System.out.println(Thread.currentThread().getName() +"\t"+ bank.money);
    }
}
class Bank{
    String bankName = "NTM";
    public volatile int money = 0;
    AtomicIntegerFieldUpdater fieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Bank.class,"money");
    public void add(Bank bank){
        fieldUpdater.getAndIncrement(bank);
    }
}

五、原子操作增强类

public class volatiles1 {
    public static void main(String[] args) {
        LongAdder longAdder = new LongAdder();
        longAdder.increment();
        longAdder.increment();
        longAdder.increment();
        System.out.println(longAdder.sum());//4
        LongAccumulator longAccumulator = new LongAccumulator((x,y) -> x + y,0);
        longAccumulator.accumulate(1);//1
        longAccumulator.accumulate(3);//4
        System.out.println(longAccumulator.get());
    }
}

LongAdder只能用来计算加法,且从零开始计算

LongAccumulato提供了自定义的函数操作。

5.1、高性能热点商品应用

public class volatiles2 {
    public static final int threadNum = 50;
    public static final int W = 10000;
    public static void main(String[] args) throws InterruptedException {
        long startTime;
        long endTime;
        Num num = new Num();
        CountDownLatch countDownLatch1 = new CountDownLatch(threadNum);
        CountDownLatch countDownLatch2 = new CountDownLatch(threadNum);
        CountDownLatch countDownLatch3 = new CountDownLatch(threadNum);
        CountDownLatch countDownLatch4 = new CountDownLatch(threadNum);
        startTime = System.currentTimeMillis();
        for (int i = 1; i <=threadNum ; i++) {
           new Thread(()->{
               try {
                   for (int j = 1; j <= 100 * W ; j++) {
                       num.getSynchronizedSum();
                   }
               } finally {
                  countDownLatch1.countDown();
               }
           },String.valueOf(i)).start();
        }
        countDownLatch1.await();
        endTime = System.currentTimeMillis();
        System.out.println("共花费:" + (endTime - startTime) + "毫秒\t" + num.num);

        startTime = System.currentTimeMillis();
        for (int i = 1; i <=threadNum ; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <= 100 * W ; j++) {
                        num.getAtomicLongSum();
                    }
                } finally {
                    countDownLatch2.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch2.await();
        endTime = System.currentTimeMillis();
        System.out.println("共花费:" + (endTime - startTime) + "毫秒\t" + num.getAtomicLong());

        startTime = System.currentTimeMillis();
        for (int i = 1; i <=threadNum ; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <= 100 * W ; j++) {
                        num.getLongAdderSum();
                    }
                } finally {
                    countDownLatch3.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch3.await();
        endTime = System.currentTimeMillis();
        System.out.println("共花费:" + (endTime - startTime) + "毫秒\t" + num.getLongAdder());

        startTime = System.currentTimeMillis();
        for (int i = 1; i <=threadNum ; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <= 100 * W ; j++) {
                        num.getLongAccumulatorSum();
                    }
                } finally {
                    countDownLatch4.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch4.await();
        endTime = System.currentTimeMillis();
        System.out.println("共花费:" + (endTime - startTime) + "毫秒\t" + num.getLongAccumulator());

    }
}
class Num{
    int num = 0;
    public synchronized void getSynchronizedSum(){
        num++;
    }
    AtomicLong atomicLong = new AtomicLong(0);
    public void getAtomicLongSum(){
        atomicLong.getAndIncrement();
    }
    public long getAtomicLong(){
       return atomicLong.get();
    }
    LongAdder longAdder = new LongAdder();
    public void getLongAdderSum(){
        longAdder.increment();
    }
    public long getLongAdder(){
       return longAdder.sum();
    }
    LongAccumulator longAccumulator = new LongAccumulator((x,y)->x+y,0);
    public void getLongAccumulatorSum(){
        longAccumulator.accumulate(1);
    }
    public long getLongAccumulator(){
        return longAccumulator.get();
    }
}

5.2、LongAdder架构图

 

 原理:Striped64中一些变量及方法

base:类似于AtomicLong中全局的value值。在没有竞争情况下数据直接累加到base上,或者calls扩容时,也需要将数据写入到base上

collide:表示扩容意向,false一定不会扩容,true可能会扩容。

cellsBusy:初始化cells或者扩容cells需要获取锁,0表示无锁状态,1:表示其他线程已经持有了锁。

casCellsBusy():通过CAS操作修改cellsBusy的值,CAS成功代表获取锁,返回true

NCPU:当前计算机CPU数量,Cell数组扩容时会使用到

getProbe();获取当前线程的hash值

advanceProbe():重置当前线程的hash值。

LongAdder的基本思路就是分散热点,将value值分散到到一个Call数组中,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行CAS操作,这样热点就被分散了,冲突的概率就小很多。如果要获取真正的long值,只要将各个槽中的变量值累加返回。

sum()会将所有Cell数组中的value和base累加作为返回值,核心的思想就是将之前AtomicLong一个value的更新压力分散到多个value中去,从而降低更新热点。

5.3、源码分析

 as:表示cells引用

b:表示获取的base值

v:表示cells数组的长度

m:表示cells数组的长度

a:表示当前线程命中的cell单元格

 public void add(long x) {
        Cell[] as; long b, v; int m; Cell a;
        //首次首线程(as = cells != null)一定是false,此时走casBase方法,以CAS的方式更新base值, 
         且只有当cas失败时,才会走到if中
        //条件1:cells不为空
        //条件2:cas操作base失败,说明其它线程先一步修改了base正在出现竞争
        if ((as = cells) != null || !casBase(b = base, b + x)) {
            //true无竞争  false表示竞争激烈,多个线程hash到同一个cell,可能要扩容
            boolean uncontended = true;
            //条件1:cells为空
            //条件2:应该不会出现
            //条件3:当前线程所在的cell为空,说明当前线程还没有更新过cell,应该初始化一个cell
            //条件4:更新当前线程所在的cell失败,说明现在竞争很激烈,多个线程hash到了同一个cell,                    
              应扩容
            if (as == null || (m = as.length - 1) < 0 ||
               //getProbe()方法返回线程的threadlocalRandomProbe字段
               //它是通过随机数生成的一个值,对于一个确定的线程这个值是固定的
                (a = as[getProbe() & m]) == null ||
                !(uncontended = a.cas(v = a.value, v + x)))
                longAccumulate(x, null, uncontended);//调用Striped64中的方法处理。
        }
    }

1、最初无竞争时只更新base

2、如果更新base失败后,首次新建一个Cell[]数组

3、当多个线程竞争同一个Cell比较激烈时,可能就要对Cell[]扩容。

longAccumulate(long x, LongBinaryOperator fn,
                          boolean wasUncontended)

x:需要增加的值,一般默认都是1

fn:默认传递的是null

wasUncontended:竞争标识,如果是false则代表有竞争,只有cells初始化之后,并且当前线程CAS竞争修改失败,才会是false

    final void longAccumulate(long x, LongBinaryOperator fn,
                              boolean wasUncontended) {
        //存储线程的probe值
        int h;
        //如果getProbe()返回0,说明随机数未初始化
        if ((h = getProbe()) == 0) {
            //使用ThreadLocalRandom为当前线程重新计算一个hash值,强制初始化
            ThreadLocalRandom.current(); // force initialization
            //重新获取prode值,hash值被重置就好比一个全新的线程一样,所以设置了wasUncontended竞争状态为true
            h = getProbe();
            //重新计算了当前线程的hash后认为此次不算是一次竞争,都未初始化,肯定还不存在竞争激烈wasUncontended竞争状态为true
            wasUncontended = true;
        }
        //如果hash取模映射得到的Cell单元不是null,则为true,此值也可以看作是扩容意向
        boolean collide = false;                // True if last slot nonempty
        for (;;) {
            Cell[] as; Cell a; int n; long v;
            //cells已经被初始化了
            if ((as = cells) != null && (n = as.length) > 0) {
                if ((a = as[(n - 1) & h]) == null) {
                    if (cellsBusy == 0) {       // Try to attach new Cell
                        Cell r = new Cell(x);   // Optimistically create
                        if (cellsBusy == 0 && casCellsBusy()) {
                            boolean created = false;
                            try {               // Recheck under lock
                                Cell[] rs; int m, j;
                                if ((rs = cells) != null &&
                                    (m = rs.length) > 0 &&
                                    rs[j = (m - 1) & h] == null) {
                                    rs[j] = r;
                                    created = true;
                                }
                            } finally {
                                cellsBusy = 0;
                            }
                            if (created)
                                break;
                            continue;           // Slot is now non-empty
                        }
                    }
                    collide = false;
                }
                else if (!wasUncontended)       // CAS already known to fail
                    wasUncontended = true;      // Continue after rehash
                else if (a.cas(v = a.value, ((fn == null) ? v + x :
                                             fn.applyAsLong(v, x))))
                    break;
                else if (n >= NCPU || cells != as)
                    collide = false;            // At max size or stale
                else if (!collide)
                    collide = true;
                
                else if (cellsBusy == 0 && casCellsBusy()) {
                    try {
                        if (cells == as) {      // Expand table unless stale
                            Cell[] rs = new Cell[n << 1];
                            for (int i = 0; i < n; ++i)
                                rs[i] = as[i];
                            cells = rs;
                        }
                    } finally {
                        cellsBusy = 0;
                    }
                    collide = false;
                    continue;                   // Retry with expanded table
                }
                h = advanceProbe(h);
            }
             //cells没有加锁且没有初始化,则尝试对它进行加锁,并初始化cells数组
            else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
                boolean init = false;
                try {                           // Initialize table
                    if (cells == as) {
                        Cell[] rs = new Cell[2];
                        rs[h & 1] = new Cell(x);
                        cells = rs;
                        init = true;
                    }
                } finally {
                    cellsBusy = 0;
                }
                if (init)
                    break;
            }
            //cells正在进行初始化,则尝试直接在基数base上进行累加操作
            else if (casBase(v = base, ((fn == null) ? v + x :
                                        fn.applyAsLong(v, x))))
                break;                          // Fall back on using base
        }
    }
public long sum() {
    Cell[] as = cells; Cell a;
    long sum = base;
    if (as != null) {
        for (int i = 0; i < as.length; ++i) {
            if ((a = as[i]) != null)
                sum += a.value;
        }
    }
    return sum;
} 

sum方法将所有Cell数组中的value和base累加作为返回值

核心思想就是将之前AtomicLong一个value的更新压力分散到多个value中去,从而降级更新热点。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/162927.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

canvas:基础知识【直线和矩形】

canvas&#xff0c;就是画布&#xff0c;是HTML5和核心技术之一&#xff0c;结合JavaScript&#xff0c;可以绘制各种各样的图形&#xff0c;比如矩形、曲线、圆形等等。另外&#xff0c;canvas可以绘制图表、动画效果、游戏开发。 基本图形汇中有直线和曲线。常见的直线图形是…

arduino rc522模块使用

rfid IC卡 先了解IC卡一些前置知识。 首先我们会有一张ic卡&#xff08;M1类型IC卡&#xff0c;一般买到的都是1K存储空间&#xff09;&#xff0c;在rc522代码中会出现这个&#xff0c;就是对IC卡进行检查PICC_TYPE_MIFARE_4K和PICC_TYPE_MIFARE_1K就是一种卡片类型不同大小…

零基础学MySQL(二)-- 表的创建,修改,删除

文章目录&#x1f388;一、创建表1️⃣基本语法2️⃣入门案例&#x1f386;二、MySQL常用数据类型1️⃣数值型&#xff08;整型&#xff09;默认有符号2️⃣数值型&#xff08;bit&#xff09;3️⃣数值型&#xff08;浮点型&#xff09;默认有符号4️⃣字符串的基本使用5️⃣字…

1584_AURIX_TC275_SMU的调试以及部分寄存器

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) 前面学习的过程中&#xff0c;突然间减速了不少。但是为了保证学习的推进&#xff0c;还是得有每天的稳定输出。我的策略是多看&#xff0c;多处理&#xff0c;之后每天整理10页标注的文档…

设计模式相关内容介绍

1.学习设计模式好处 提高编程能力、思维能力、设计能力程序设计更加标准化、代码编制更加工程化&#xff0c;软件开发效率大大提高&#xff0c;缩短项目周期设计的代码可重用性高、可读性强、可靠性高、 灵活性好、可维护性强 2.设计模式分类 创建型模式 提供创建对象的机制…

一文读懂工业级交换机的规范使用

工业交换机具备电信级特性特点&#xff0c;可承受严苛的工作环境&#xff0c;产品种类丰富多彩&#xff0c;交换机配置灵便&#xff0c;可以满足各类工业应用的应用标准。那么&#xff0c;大家使用工业级交换机的过程当中应该如何规范使用呢&#xff1f; 工业级交换机其实质运…

蓝队攻击的四个阶段(四)

目录 一&#xff0c; 外网纵向突破 1.1 何为外网纵向突破 1.2外网纵向突破的主要工作 二&#xff0c; 外网纵向突破的途径 1. Web 网站 2.外部邮件系统 3.边界网络设备 4.外部应用平台 三&#xff0c;内网横向拓展 1. 1何为内网横向拓展 1.2 内网横向拓展的主要工作 …

电商价格监测,关注这些,才算实际到手价

品牌控价的第一项工作&#xff0c;是先找出低价乱价链接&#xff0c;这就需要进行电商价格监测。但是我们搜索品牌链接的时候&#xff0c;会发现网页上的价格是多种多样&#xff1a;有原价&#xff08;但是划掉了&#xff09;、促销价、折扣价、惊喜价&#xff0c;优惠活动也是…

localStorage

localStorage localStorage了解 有些数据确实需要存储在本地&#xff0c;但是它却不需要发送到服务器&#xff0c;所以并不适合放在cookie中 localStorage 也是一种浏览器存储数据的方式&#xff08;本地存储&#xff09;&#xff0c;它只是存储在本地&#xff0c;不会发送…

【Linux】进程间通信(1)

信号 什么是信号&#xff1f;信号是给程序提供一种可以处理异步事件的方法&#xff0c;它利用软件中断来实现。不能自定义信号&#xff0c;所有信号都是系统预定义的。 信号由谁产生&#xff1f; 由shell终端根据当前发生的错误&#xff08;段错误、非法指令等&#xff09;Ctr…

商品详情的APP原数据接口测试

一、原数据接口的来源&#xff1a; 原数据接口来源于手机端&#xff0c;随着智能化的发展与普及&#xff0c;越来越多的人都是使用智能手机&#xff0c;这样极大的方便了人民的生活&#xff0c;各大电商平台看准了这个商家&#xff0c;把目光都瞄准这个商机&#xff0c;伴随而…

BP靶场中SQL注入练习

BP靶场中SQL注入练习1.Bp靶场介绍1.1.访问靶场1.2.注意事项2.SQL注入靶场2.1.注意事项2.2.检索隐藏数据2.2.1.开启靶场2.2.2.点击礼物2.2.3.测试类型2.2.4.爆出全部物品(包括隐藏)2.3.登录逻辑2.3.1.开启靶场2.3.2.登录账户2.3.3.注释验证2.3.4.成功登陆2.4.判断列2.4.1.开启靶…

会话技术--cookie和session

一、会话跟踪技术的概述 对于会话跟踪这四个词&#xff0c;我们需要拆开来进行解释&#xff0c;首先要理解什么是会话&#xff0c;然后再去理解什么是会 话跟踪: 会话:用户打开浏览器&#xff0c;访问web服务器的资源&#xff0c;会话建立&#xff0c;直到有一方断开连接&#…

变量、作用域与内存

目录 原始值与引用值 动态属性 复制值 传递参数 确定类型 执行上下文与作用域 作用域链增强 变量声明 1.使用var 的函数作用域声明 2. 使用let 的块级作用域声明 3.使用const 的常量声明 标识符查找 垃圾回收 标记清理&#xff08;最常用&#xff09; 引用计数 内…

2022__我的嵌入式入坑之路

目录 一、学习篇 51单片机&#xff1a; python爬虫&#xff1a; stm32单片机&#xff1a; ad&#xff1a; 立创EDA&#xff1a; openmv&#xff1a; ardunio&#xff1a; ESP32&#xff1a; 汇编语言&#xff1a; ROS&#xff1a; FreeRTOS&#xff1a; matlab&a…

【学习】大数据关键技术

学习内容描述&#xff1a; 大数据涉及的四个环节是什么&#xff1f; 云计算服务的三种服务类型是什么&#xff1f; 重点知识&#xff1a; 大数据涉及的四个环节&#xff1a;1、数据采集&#xff1b;2、数据存储&#xff1b;3、数据管理&#xff1b;4、数据分析与挖掘。云计算…

大型智慧灌区信息化管理系统云平台 智慧灌区信息化管理系统解决方案

平升电子大型智慧灌区信息化管理系统云平台/智慧灌区信息化管理系统解决方案&#xff0c;对灌区的渠道水位、流量、水雨情、土壤墒情、气象等信息进行监测&#xff0c;同时对泵站、闸门进行远程控制&#xff0c;对重点区域进行视频监控&#xff0c;实现了信息的采集、统计、分析…

基于pyautogui的自动识别定位原神风物之诗琴按键弹奏程序

前言&#xff1a;为了学习pyautogui这个库的使用&#xff0c;我准备用它做点东西。比如一个自动弹琴的程序。不过这个琴不是现实里的琴&#xff0c;而是原神里的风物之诗琴。&#xff08;这里有个网页版模拟器可以试试&#xff1a;风物之诗琴模拟器 (haveyouwantto.github.io)&…

Spring 监听器listener原理

1.创建本地事件和事件监听器/*** 事件类*/ public class MyEventA extends ApplicationEvent {private static final long serialVersionUID 1L;public MyEventA(Object source) {super(source);System.out.println("MyEventA 构造方法被执行了...");}public void o…

基于matlab开发的车牌检测与字符分割项目附源码

文章目录1 任务概述项目完整matlab源码2 基本流程2.1 车牌定位2.1.1 图像预处理2.1.2 边缘检测2.1.3 形态学操作2.1.4 重操作判断2.1.5 区域选择2.1.6 倾斜校正2.1.7 精确选择2.2 字符分割2.3 字符识别3 结果分析3.1 车牌定位3.2 字符分割3.3 字符识别4 总结5 参考资料1 任务概…