保证原子性的几种方式,你都知道吗???

news2024/11/27 8:31:08

1. 前言

今天主要是从实战 + 浅谈原理的角度来说下,并发线程下原子性的几种处理方式,好了废话不多说了,接下来让我们看看吧

2. 开始

在开始之前需要提问下大家, 代码i ++; 能保持原子性吗??? 是不是一句话就执行ok了呢??? 接下来看下字节码分析:。

public class T01_Thread_Test01 {
    static int i = 0;
    public static void main(String[] args) {
        i ++;
    }
}

在这里插入图片描述

通过上述的截图可以看到,短短一句话i++ 会被翻译为很多条语句,在多线程的情况下,执行到任何语句时都会被别的线程打断,所以很难保证执行结果的一致性。

所以为了保证原子性,我们就要代码执行过程中不可拆分。

2.1 synchronized

public class T03_Thread_Synchronized {
    static int count = 0;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            synchronized (T03_Thread_Synchronized.class) {
                for (int i = 0; i < 100000; i++) {
                    count++;
                }
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (T03_Thread_Synchronized.class) {
                for (int i = 0; i < 100000; i++) {
                    count ++;
                }
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(count);
    }
}

上锁是一定能保证原子性的。在保证某个线程拿到锁后,别的线程只能处于blocked状态。是无法构成资源竞争的。
其实锁的本质也是并发线程的序列化操作

在这里插入图片描述

通过上述的截图中也可以看出。添加synchronized之后,从代码层面就相当于上锁了。等语句执行结果后会自动释放锁。

2.2 CAS

  • CAC 全称是compare and swap. 也称为自旋锁,无锁,乐观锁等。
  • 其大致的原理是:在替换内存的某个位置的值时,会判断下运算之前的值 跟 内存的值是否保持一致(为了避免别的线程也修改过此值)。 如果一致的话,直接替换。反之拿到新值继续 比较-替换

在这里插入图片描述

CAS在Java代码中如何体现呢??? Java中基于Unsafe的类提供了对CAS的操作的方法,JVM会帮助我们将方法实现CAS汇编指令

public class T02_Thread_CAS {
    private static AtomicInteger atomicInteger = new AtomicInteger(0);
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 100000; i++) {
                atomicInteger.incrementAndGet();
            } 
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 100000; i++) {
                atomicInteger.incrementAndGet();
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(atomicInteger);
    }
}
  • 通过上述的代码我们会发现,整个过程中我们并没有用到锁。而是使用Java提供的API incrementAndGet。而incrementAndGet 其实就是基于类unsafe 来实现的。
    在这里插入图片描述
  • 通过下列字节码可以看到。以下的代码其实就是自旋的过程
    在这里插入图片描述

2.2.1 CAS不足之处:

2.2.1.1 ABA 问题

ABA问题:问题如下,可以引入版本号的方式,来解决ABA的问题。Java中提供了一个类在CAS时,针对各个版本追加版本号的操作。 AtomicStampeReferenceimage.png

2.2.1.2 自旋时间过长的问题

  • 可以指定CAS一共循环多少次,如果超过这个次数,直接失败/或者挂起线程。(自旋锁、自适应自旋锁)
  • 可以在CAS一次失败后,将这个操作暂存起来,后面需要获取结果时,将暂存的操作全部执行,再返回最后的结果

2.3 lock锁

Lock锁是在JDK1.5由Doug Lea研发的,他的性能相比synchronized在JDK1.5的时期,性能好了很多多,但是在JDK1.6对synchronized优化之后,性能相差不大,但是如果涉及并发比较多时,推荐ReentrantLock锁,性能会更好

public class T05_Thread_ReentrantLock {

    static ReentrantLock lock = new ReentrantLock();
    private static int count = 0;

    public static void add() {
        try {
            lock.lock();
            for (int i = 0; i < 100000; i++) {
                count ++;
            }
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t01 = new Thread(T05_Thread_ReentrantLock::add);
        Thread t02 = new Thread(T05_Thread_ReentrantLock::add);

        t01.start();
        t02.start();

        t01.join();
        t02.join();

        System.out.println(count);
    }
}

注意:因为lock锁无法自动释放锁。所以unlock释放锁的从操作必须用finally 包裹下,表示释放锁的操作一定会执行。

2.4 ThreadLocal

其他方式都是允许线程操作共享资源,才会形成了资源竞争。但是ThtreadLocal 是避免操作临界值的,每个值只单独在自己的线程中。这样就避免了资源竞争了

public class T06_Thread_ThreadLocal {

    static ThreadLocal t1 = new ThreadLocal();
    static ThreadLocal t2 = new ThreadLocal();

    public static void main(String[] args) {
        t1.set(123);
        t2.set(456);
        t1.set(345);

        new Thread(() -> {
            System.out.println("线程:" + t1.get());
            System.out.println("线程" + t2.get());
        }).start();

        System.out.println("main线程" + t1.get());
        System.out.println("main线程" + t2.get());
    }
}

2.4.1 ThreadLocal实现原理

  • 每个Thread中都存储着一个成员变量,ThreadLocalMap
  • ThreadLocal本身不存储数据,像是一个工具类,基于ThreadLocal去操作ThreadLocalMap
  • ThreadLocalMap本身就是基于Entry[]实现的,因为一个线程可以绑定多个ThreadLocal,这样一来,可能需要存储多个数据,所以采用Entry[]的形式实现。
  • 每一个线程都自己独立的ThreadLocalMap,再基于ThreadLocal对象本身作为key,对value进行存取
  • ThreadLocalMap的key是一个弱引用,弱引用的特点是,即便有弱引用,在GC时,也必须被回收。这里是为了在ThreadLocal对象失去引用后,如果key的引用是强引用,会导致ThreadLocal对象无法被回收

2.4.2 ThreadLocal内存泄漏问题

  • 如果ThreadLocal引用丢失,key因为弱引用会被GC回收掉,如果同时线程还没有被回收,就会导致内存泄漏,内存中的value无法被回收,同时也无法被获取到。
  • 只需要在使用完毕ThreadLocal对象之后,及时的调用remove方法,移除Entry即可

image.png

3. 结束

关于保证原子性的几种方式就列举这么多了。如果大家不同的看法 欢迎请在评论区内留言,欢迎一起进步

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

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

相关文章

C++——命名空间,输入输出,缺省参数

✅<1>主页&#xff1a;我的代码爱吃辣 &#x1f4c3;<2>知识讲解&#xff1a;数据结构——二叉树 &#x1f525;<3>创作者&#xff1a;我的代码爱吃辣 ☂️<4>开发环境&#xff1a;Visual Studio 2022 &#x1f4ac;<5>前言&#xff1a;补充C语言…

Java 诊断利器 Arthas JVM命令

一、jvm 相关命令介绍 命令说明dashboard当前系统的实时数据面板getstatic查看类的静态属性heapdumpdump java heap, 类似 jmap 命令的 heap dump 功能jvm查看当前 JVM 的信息logger查看和修改 loggermbean查看 Mbean 的信息memory查看 JVM 的内存信息ognl执行 ognl 表达式per…

【Android春招】Android基础day2

一、填空题 1&#xff0e;除了开启开发者选项之外&#xff0c;还需打开手机上的 _ 开关&#xff0c;然后才能在手机上调试App。 USB调试 2&#xff0e;App开发的两大技术路线包括 _和混合开发。 原生开发 3&#xff0e;App工程的编译配置文件名为 _。 build.gradle 4&#xff0…

挂载光盘,配置yum源并且安装http软件包 ansible(4)

目录 一、挂载本地光盘到/mnt 二、配置yum源 一、挂载本地光盘到/mnt 第一步&#xff1a; 使用mount模块 注&#xff1a;fstype代表文件格式 二、配置yum源 第一种方法&#xff1a; 使用yum_repository模块 检验是否有对应文件在受控主机node1受控主机&#xff1a;node2受控…

Zipkin数据持久化配置

上一篇我们了解了Zipkin的基础知识以及Zipkin的服务端搭建。 在使用过程中很多同学发现了他的秘密&#xff0c;Zipkin模式将数据保存在内存中&#xff0c;当我们重启后&#xff0c;追踪数据便会丢失。其实&#xff0c;Zipkin也支持将追踪数据保存到MySql或者ES中。 持久化到M…

大数据基础平台搭建-(四)HBbase集群HA+Zookeeper搭建

大数据基础平台搭建-&#xff08;四&#xff09;HBbase集群HAZookeeper搭建 大数据平台系列文章&#xff1a; 1、大数据基础平台搭建-&#xff08;一&#xff09;基础环境准备 2、大数据基础平台搭建-&#xff08;二&#xff09;Hadoop集群搭建 3、大数据基础平台搭建-&#xf…

Linux学习笔记——Nginx安装部署

5.3、Nginx安装部署 5.3.1、简介 Nginx&#xff08;engine x&#xff09;是一个高性能的HTTP和反向代理Web服务器&#xff0c;同时也提供了IMAP/POP3/SMTP服务。 同Tomcat一样&#xff0c;Nginx可以托管用户编写的WEB应用程序成为可访问的网页服务&#xff0c;同时也可以作为…

二十七、Docker (3)

&#x1f33b;&#x1f33b; 目录一、java开发者Docker常用镜像容器创建&#xff08;本地&#xff09;1.1 资源上传1.2 还原部署1.2.1 部署jdk1.2.2 部署tomcat1.2.3 部署nginx1.2.4 部署mysql二、java开发者Docker常用镜像容器创建(官网拉取)2.1 jdk部署2.1.1 拉取jdk镜像2.1.…

verilog学习笔记- 7)verilog程序框架

目录 注释&#xff1a; 关键字&#xff1a; Verilog常用的关键字&#xff1a; 程序框架&#xff1a; 注释&#xff1a; Verilog HDL 中有两种注释的方式&#xff0c;一种是以“/*”符号开始&#xff0c;“*/”结束&#xff0c;在两个符号之间的语句都 是注释语句&#xff…

7-10 集合相似度

给定两个整数集合&#xff0c;它们的相似度定义为&#xff1a;Nc​/Nt​100%。其中Nc​是两个集合都有的不相等整数的个数&#xff0c;Nt​是两个集合一共有的不相等整数的个数。你的任务就是计算任意一对给定集合的相似度。 输出格式&#xff1a; 对每一对需要计算的集合&…

DaVinci:键 - 键混合器

调色页面&#xff1a;键Color&#xff1a;Key当选中一个键混合器节点时&#xff0c;键 Key调板上对应显示键混合器 Key Mixer的相关选项。键混合器节点默认有两个输入链接。可在键混合器节点上右击选择“添加”或“移除”一个输入。所有的输入链接和惟一的输出链接均可在键调板…

Elasticsearch在各大互联网公司大量真实的应用案例

国内现在有大量的公司都在使用 Elasticsearch&#xff0c;包括携程、滴滴、今日头条、饿了么、360安全、小米、vivo等诸多知名公司。 除了搜索之外&#xff0c;结合Kibana、Logstash、Beats&#xff0c;Elastic Stack还被广泛运用在大数据近实时分析领域&#xff0c;包括日志分…

SQL如何在数据库中执行

数据库的服务端&#xff0c;可分为执行器(Execution Engine) 和 存储引擎(Storage Engine) 两部分&#xff1a; 执行器负责解析SQL执行查询存储引擎负责保存数据 1 SQL如何在执行器中执行 # 查询用户ID大于50的用户的所有订单 SELECT u.id AS user_id, u.name AS user_name,…

详解回调函数

目录 前言 一、 实现一个简单的计算器 1.1 - 代码一 1.2 - 代码二 二、qsort 函数的介绍 三、改进冒泡排序函数 前言 回调函数就是一个通过函数指针调用的函数。如果把函数的指针&#xff08;地址&#xff09;作为参数传递给另一个函数&#xff0c;当这个指针被用来调用其…

微信公众号调用扫一扫功能

手把手教你调用微信扫一扫&#xff0c;三分钟包会_前端人的博客-CSDN博客_调用微信扫一扫 第一次搞公众号&#xff0c;还以为跟上回调用企业微信扫一扫一样。。。调起扫一扫功能的过程自然是不同的&#xff0c;要注意的地方还挺多&#xff0c;记录一下 。 其实&#xff0c;在使…

2023最新网络安全自学路线,内容涵盖3-5年技能提升

前言 先预祝大家新年快乐&#xff01; 【一一帮助网络安全入门和提升点这里一一】 01 什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究…

改进MBR(操作显卡来输出字符)

文章目录前言前置知识mbr.S代码实验操作前言 本博客记录《操作系统真象还原》第二章第一个实验操作~ 实验环境&#xff1a;ubuntu18.04VMware &#xff0c; Bochs下载安装 实验内容&#xff1a;在屏幕上打印字符串【采用的方式是直接修改显存实现】 实验原因&#xff1a;mb…

Flutter性能优化

原理 Flutter的架构主要分成三层&#xff1a;Framework&#xff0c;Engine和Embedder Framework 使用 dart 实现&#xff0c;包括 Material Design 风格的Widget&#xff0c;Cupertino(针对iOS)风格的Widgets&#xff0c;UI/文本/图片/按钮等基础 Widgets&#xff0c;渲染&…

设计模式学习(一):Bridge桥接模式

一、什么是Bridge模式Bridge模式的作用是在“类的功能层次结构”和“类的实现层次结构”之间搭建桥梁。1.1 类的功能层次结构主要作用就是增加新的功能。当我们要增加新的功能时&#xff0c;我们可以从各个层次的类中找出最符合自己需求的类&#xff0c;然后以它为父类编写子类…

(Week 10)最小生成树(C++,prim,Kruskal,并查集)

文章目录Einstein学画画&#xff08;C&#xff0c;欧拉路&#xff09;题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示解题思路&#xff1a;并查集&#xff08;C&#xff09;[蓝桥杯 2017 国 C] 合根植物&#xff08;C&#xff0c;并查集&#xff09;题目描述输入格…