7 原子类

news2024/9/28 9:25:55

Java.util.concurrent.atomic
在这里插入图片描述
7.2 没有CAS之前
多线程环境中不使用原子类保证线程安全i++(基本数据类型)

class Test {
        private volatile int count = 0;
        //若要线程安全执行执行count++,需要加锁
        public synchronized void increment() {
                  count++;
        }

        public int getCount() {
                  return count;
        }
}

7.3 使用CAS之后
多线程环境中使用原子类保证线程安全i++(基本数据类型)---------->类似于乐观锁

class Test2 {
        private AtomicInteger count = new AtomicInteger();

        public void increment() {
                  count.incrementAndGet();
        }
      //使用AtomicInteger之后,不需要加锁,也可以实现线程安全。
       public int getCount() {
                return count.get();
        }
}

7.4 是什么?
CAS(compare and swap),中文翻译为比较并交换,实现并发算法时常用到的一种技术,用于保证共享变量的原子性更新,它包含三个操作数—内存位置、预期原值与更新值。
执行CAS操作的时候,将内存位置的值与预期原值进行比较:
● 如果相匹配,那么处理器会自动将该位置更新为新值
● 如果不匹配,处理器不做任何操作,多个线程同时执行CAS操作只有一个会成功。
在这里插入图片描述

public class CASDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(5);
        System.out.println(atomicInteger.compareAndSet(5, 2022) + "\t" + atomicInteger.get());//true	2022
        System.out.println(atomicInteger.compareAndSet(5, 2022) + "\t" + atomicInteger.get());//false	2022
    }
}

在这里插入图片描述
7.5 CAS底层原理?谈谈对Unsafe类的理解?
7.5.1 Unsafe
Unsafe类是CAS的核心类,由于Java方法无法直接访问底层系统,需要通过本地(native)方法来访问,Unsafe相当于一个后门,基于该类可以直接操作特定内存的数据。Unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因此Java中CAS操作的执行依赖于Unsafe类的方法。
注意:Unsafe类中的所有方法都是native修饰的,也就是说Unsafe类中的所有方法都直接调用操作系统底层资源执行相应任务。
问题:我们知道i++是线程不安全的,那AtomicInteger.getAndIncrement()如何保证原子性?
AtomicInteger类主要利用CAS+volatile和native方法来保证原子操作,从而避免synchronized的高开销,执行效率大为提升:
在这里插入图片描述
CAS并发原语体现在Java语言中就是sun.misc.Unsafe类中的各个方法。调用Unsafe类中的CAS方法,JVM会帮我们实现出CAS汇编指令。这是一种完全依赖于硬件的功能,通过它实现了原子操作。再次强调,由于CAS是一种系统原语,原语属于操作系统用语范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题。

7.5.2 源码分析

在这里插入图片描述
7.5.3 底层汇编
在这里插入图片描述
JDK提供的CAS机制,在汇编层级会禁止变量两侧的指令优化,然后使用compxchg指令比较并更新变量值(原子性)
总结:
● CAS是靠硬件实现的从而在硬件层面提升效率,最底层还是交给硬件来保证原子性和可见性
● 实现方式是基于硬件平台的汇编指令,在inter的CPU中,使用的是汇编指令compxchg指令
● 核心思想就是比较要更新变量V的值和预期值E,相等才会将V的值设为新值N,如果不相等自旋再来

7.6 原子引用

在这里插入图片描述

@Data
@AllArgsConstructor
@NoArgsConstructor
class User {
    String userName;
    int age;
}

public class AtomicReferenceDemo {
    public static void main(String[] args) {
        AtomicReference<User> atomicReference = new AtomicReference<>();
        User z3 = new User("z3", 22);
        User li4 = new User("li4", 25);

        atomicReference.set(z3);
        System.out.println(atomicReference.compareAndSet(z3, li4) + "\t" + atomicReference.get().toString());//true	User(userName=li4, age=25)
        System.out.println(atomicReference.compareAndSet(z3, li4) + "\t" + atomicReference.get().toString());//false	User(userName=li4, age=25)

    }
}

7.7 CAS与自旋锁,借鉴CAS思想
7.7.1 是什么?
CAS是实现自旋锁的基础,CAS利用CPU指令保证了操作的原子性,以达到锁的效果,至于自旋锁—字面意思自己旋转。是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,当线程发现锁被占用时,会不断循环判断锁的状态,直到获取。这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。

7.7.2 自己实现一个自旋锁spinLockDemo
题目:实现一个自旋锁,借鉴CAS思想
通过CAS完成自旋锁,A线程先进来调用myLock方法自己持有锁5秒钟,B随后进来后发现当前有线程持有锁,所以只能通过自旋等待,直到A释放锁后B随后抢到。

public class SpinLockDemo {

    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void lock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "\t --------come in");
        while (!atomicReference.compareAndSet(null, thread)) {

        }
    }

    public void unLock() {
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread, null);
        System.out.println(Thread.currentThread().getName() + "\t --------task over,unLock.........");
    }

    public static void main(String[] args) {
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        new Thread(() -> {
            spinLockDemo.lock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLockDemo.unLock();
        }, "A").start();


        try {
            TimeUnit.MILLISECONDS.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            spinLockDemo.lock();
            spinLockDemo.unLock();
        }, "B").start();
    }
}
/**
 * A	 --------come in
 * B	 --------come in
 * A	 --------task over,unLock.........
 * B	 --------task over,unLock.........
 */

7.8 CAS缺点
7.8.1 循环时间长开销很大
在这里插入图片描述
● getAndAddInt方法有一个do while
● 如果CAS失败,会一直进行尝试,如果CAS长时间一直不成功,可能会给CPU带来很大开销
7.8.2 引出来ABA问题?
● ABA问题怎么产生的?
○ CAS算法实现一个重要前提需要提取出内存中某时刻的数据并在当下时刻比较并替换,那么在这个时间差类会导致数据的变化。
○ 比如说一个线程1从内存位置V中取出A,这时候另一个线程2也从内存中取出A,并且线程2进行了一些操作将值变成了B,然后线程2又将V位置的数据变成A,这时候线程1进行CAS操作发现内存中仍然是A,预期ok,然后线程1操作成功--------尽管线程1的CAS操作成功,但是不代表这个过程就是没有问题的。
● 版本号时间戳原子引用
在这里插入图片描述

@Data
@AllArgsConstructor
@NoArgsConstructor
class Book {
    private int id;
    private String bookName;
}

public class AtomicStampedReferenceDemo {
    public static void main(String[] args) {
        Book javaBook = new Book(1, "javaBook");
        AtomicStampedReference<Book> atomicStampedReference = new AtomicStampedReference<>(javaBook, 1);
        System.out.println(atomicStampedReference.getReference() + "\t" + atomicStampedReference.getStamp());

        Book mysqlBook = new Book(2, "mysqlBook");
        boolean b;
        b = atomicStampedReference.compareAndSet(javaBook, mysqlBook, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
        System.out.println(b + "\t" + atomicStampedReference.getReference() + "\t" + atomicStampedReference.getStamp());

        b = atomicStampedReference.compareAndSet(mysqlBook, javaBook, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
        System.out.println(b + "\t" + atomicStampedReference.getReference() + "\t" + atomicStampedReference.getStamp());
    }
}
/**
 * Book(id=1, bookName=javaBook)	1
 * true	Book(id=2, bookName=mysqlBook)	2
 * true	Book(id=1, bookName=javaBook)	3
 */

多线程情况下演示AtomicStampedReference解决ABA问题

public class ABADemo {
    static AtomicInteger atomicInteger = new AtomicInteger(100);
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);

    public static void main(String[] args) {
//        abaHappen();//true	2023
        /**
         * t3	首次版本号: 1
         * t4	首次版本号: 1
         * t3	2次版本号: 2
         * t3	3次版本号: 3
         * false	100	3
         */
        abaNoHappen();

    }

    private static void abaNoHappen() {
        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t" + "首次版本号: " + stamp);
            try {
                TimeUnit.MILLISECONDS.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicStampedReference.compareAndSet(100, 101, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "\t" + "2次版本号: " + atomicStampedReference.getStamp());
            atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "\t" + "3次版本号: " + atomicStampedReference.getStamp());
        }, "t3").start();


        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t" + "首次版本号: " + stamp);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean b = atomicStampedReference.compareAndSet(100, 200, stamp, stamp + 1);
            System.out.println(b + "\t" + atomicStampedReference.getReference() + "\t" + atomicStampedReference.getStamp());
        }, "t4").start();
    }

    private static void abaHappen() {
        new Thread(() -> {
            atomicInteger.compareAndSet(100, 101);
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicInteger.compareAndSet(101, 100);
        }, "t1").start();


        new Thread(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicInteger.compareAndSet(100, 2023) + "\t" + atomicInteger.get());//true	2023
        }, "t2").start();
    }
}

一句话:比较加版本号一起上

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

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

相关文章

二叉树前序遍历:在树叶掉落前,寻找根的方向

本篇博客会讲解力扣“144. 二叉树的前序遍历”的解题思路&#xff0c;这是题目链接。 先来审题&#xff1a; 由于本篇博客会使用C语言来实现这道题&#xff0c;最简单的解法自然是使用递归。所谓前序遍历&#xff0c;即按照“根、左子树、右子树”的顺序来遍历&#xff0c;当…

【新版】系统架构设计师 - 数据库系统

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录 架构 - 数据库系统考点摘要数据库系统模式数据库视图数据模型&#xff08;基本数据模型&#xff09;数据库完整性约束关系模型关系代数规范化理论候选键、主键、外键、主属性&#xff0c;非主属性求候选键…

【笔记】最优解人生

死前归零 如果在死亡之前没有将赚到的钱花完&#xff0c;那么剩下那些没花完的钱&#xff0c;就是你白白浪费的生命能量。 60岁以后&#xff0c;虽然医疗开支变大&#xff0c;但娱乐&#xff0c;衣物的开始会变小&#xff0c;总体上开销会越来越少。 如何“死前归零”呢&…

两两交换链表中的节点

题目描述 代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *next) : val(x), next(next…

【备战秋招】每日一题:4月15日美团春招:题面+题目思路 + C++/python/js/Go/java带注释

2023大厂笔试模拟练习网站&#xff08;含题解&#xff09; www.codefun2000.com 最近我们一直在将收集到的各种大厂笔试的解题思路还原成题目并制作数据&#xff0c;挂载到我们的OJ上&#xff0c;供大家学习交流&#xff0c;体会笔试难度。现已录入200道互联网大厂模拟练习题&a…

简单实用折叠面板可以折叠收起展开内容区域

前端简单实用折叠面板可以折叠收起展开内容区域, 下载完整代码请访问uni-app插件市场地址: https://ext.dcloud.net.cn/plugin?id12860 效果图如下: 代码如下: # 简单实用折叠面板可以折叠收起展开内容区域。 #### 使用方法 使用方法 <!-- leftText:做标题 rigText:…

智能算法终极大比拼,以CEC2005测试函数为例,十种智能算法直接打包带走,不含任何套路!

包含人工蜂群(ABC)、灰狼(GWO)、差分进化(DE)、粒子群(PSO)、麻雀优化(SSA)、蜣螂优化(DBO)、白鲸优化(BWO)、遗传算法(GA)、粒子群算法(PSO)&#xff0c;基于反向动态学习的差分进化算法&#xff0c;共十种算法&#xff0c;直接一文全部搞定&#xff01; 由于上一篇代码&…

赋能矿山 | KaiwuDB 智慧矿山解决方案

行业背景 随着勘探和矿产开发技术的提高以及能源需求量的大幅增加&#xff0c;矿山开发速度持续加快。随之而来的诸多弊端&#xff0c;如矿山资源综合利用率低、管理方式粗放、安全和环境污染等问题日益突出&#xff0c;使得矿业智能化建设迫在眉睫。 近年来&#xff0c;政府…

【算法与数据结构】160、LeetCode相交链表

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;这道题题目蛮长的。说的大概意思就是对比指针地址&#xff0c;只要相等了&#xff0c;那就是相交节点。…

华为OD机试之执行时长(Java源码)

执行时长 题目描述 为了充分发挥GPU算力&#xff0c;需要尽可能多的将任务交给GPU执行&#xff0c;现在有一个任务数组&#xff0c;数组元素表示在这1秒内新增的任务个数且每秒都有新增任务。 假设GPU最多一次执行n个任务&#xff0c;一次执行耗时1秒&#xff0c;在保证GPU不空…

论文解读 | 机器人路径跟踪的受控纯追踪法

原创 | 文 BFT机器人 01 摘要 这篇文章是关于机器人路径跟踪的受控纯追踪算法的研究。文章介绍了传统的纯追踪算法以及相关变体&#xff0c;并提出了受控纯追踪算法作为本文的主要贡献。该算法旨在适应实际环境中的服务和工业移动机器人&#xff0c;提供方法来调整机器人的平移…

关于spring boot报javax/servlet/MultipartConfigElement错误以及的问题

springboot在启动时报java.lang.NoClassDefFoundError: javax/servlet/MultipartConfigElement 解决方法&#xff1a;将pom.xml文件中的provided删掉

盛元广通生物化学重点实验室化学品信息化安全管理系统

生物化学重点实验室是国家基础研究和高技术研究的重要基地&#xff0c;是培养和造就高层次创新型人才的重要基地。为保障实验室化学品安全使用&#xff0c;实验人员可通过现场或移动端管理系统实现化学品安全使用与存储。盛元广通生物化学重点实验室化学品信息化安全管理系统具…

方波信号拆分及合成

1. 前言 数字处理器产生的都是方波信号&#xff0c; 做EMC会有各种高频率的辐射超标。比如下图为EMC测试辐射图&#xff0c;其中480MHz左右频率超标&#xff0c;那么就需要分析这个频率是谁产生的&#xff0c;但是整个系统都没有谁使用了这么高的频率信号。但是一般根据经验都知…

mysql-explain-其他

eq_ref 主键和唯一索引的所有部分被连接使用&#xff0c;最多只会返回一条符合条件的记录【主键关联查询】 ref: 根节点二分查找 没有使用唯一索引&#xff0c;而是走了普通索引【二级索引】或者唯一索引的部分前缀&#xff0c;有可能查出多个符合条件的行。 range 范围扫…

全网最全的Python教程(非常详细),从零基础入门到上岗!

Python的火&#xff0c;有目共睹。Python陆续登上了IEEE、PYPL排行榜单之首&#xff0c;对于Python的崛起TIOBE排行榜也给予了回应&#xff1a;由于全球流行度在过去一年中涨幅最高&#xff01; 同时人工智能的兴起&#xff0c;也给了Python无限可能&#xff0c;让Python越来越…

Systick实现中断-GD32

定时器中断-GD32 include "gd32f10x.h" #include "systick.h" #include "LED.h"volatile static uint32_t delay_reload; volatile static uint32_t delay_value;/*!\brief configure systick\param[in] none\param[out] none\retval …

asp.net试卷自动生成系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net试卷自动生成系统 是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c#语言开发 asp.net试卷自动生成系统VS开发sq…

Java 泛型:概念、语法和应用

文章目录 一、什么是 Java 泛型&#xff1f;二、为什么要使用泛型三、Java 泛型的语法四、Java 泛型的应用五、Java 泛型的示例六、总结 随着 Java 编程语言的不断发展和更新&#xff0c;Java 1.5 版本引入了一项全新的特性——Java 泛型&#xff08;Java Generic&#xff09;&…