如何保证线程的原子性

news2024/11/26 2:36:41

线程的原子性是指:一个或者一系列指令,它的操作是不可中断的,一旦开始是不允许被其他CPU或线程来中断。

我们来看下述代码:ThreadAtomicityDemo类中有个初始值为0的Integer类型的静态变量count,还有一个每次sleep一毫秒,并且使count加一的静态方法increase
在main方法中,循环1000次,启动1000个线程来执行 increase方法。
请问在执行过1000个线程,每次使count加一后,count是否为1000?

public class ThreadAtomicityDemo {
    public static int count = 0;
    public static void increase(){
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        count ++;
    }

    public static void main(String[] args) throws InterruptedException {
        for(int i=0;i<1000;i++){
            new Thread(()->ThreadAtomicityDemo.increase()).start();
        }
        Thread.sleep(2000);
        System.out.println("The count is : " + count);
    }
}

执行结果:

The count is : 984

Process finished with exit code 0

结果是小于1000的随机数。


上述代码的原子性体现在 count++ 上。
count++ 虽然只是一行代码,但实际上在底层执行CPU指令时,count++ 会转化为三个指令。我们一起来看一下:
在class目录下执行 D:\open_source\MyBatis\MyThread\target\classes\demo>javap -v ThreadAtomicityDemo.class
可以看到字节码中的 count++ 指令,节选关键部分如下:

...
  public static void increase();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=0
         0: lconst_1
         1: invokestatic  #2                  // Method java/lang/Thread.sleep:(J)V
         4: goto          12
         7: astore_0
         8: aload_0
         9: invokevirtual #4                  // Method java/lang/InterruptedException.printStackTrace:()V
        12: getstatic     #5                  // Field count:I
        15: iconst_1
        16: iadd
        17: putstatic     #5                  // Field count:I
        20: return
      Exception table:
         from    to  target type
             0     4     7   Class java/lang/InterruptedException
      LineNumberTable:
        line 7: 0
        line 10: 4
        line 8: 7
        line 9: 8
        line 11: 12
        line 12: 20
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            8       4     0     e   Ljava/lang/InterruptedException;
      StackMapTable: number_of_entries = 2
        frame_type = 71 /* same_locals_1_stack_item */
          stack = [ class java/lang/InterruptedException ]
        frame_type = 4 /* same */
......

我们可以发现,count++在Java里表现出来的指令和CPU真正执行时的指令时有差距的。
在这里插入图片描述
由于CPU层面是由三条语句来完成count++,所以中多线程的情况下,就很难保证它的原子性了。
在这里插入图片描述
如上图所示,

  1. 当Thread-1开始执行,读取count的值0;
  2. 线程切换至Thread-2,读取count的值,当前还是0;
  3. 线程2继续执行count++,得到count的值为1,写入寄存器中;
  4. 这是线程切换回Thread-1,线程1从刚才中断的地方继续,count++,得到count值也是1
  5. Thread1将count=1 写入寄存器。
  6. 当前经过Thread1和Thread2两个线程执行后,count值最终为1,并非是2.

同理,1000个线程结束后,count值就为一个小于1000 的数。


如何保证多线程中的原子性,可以使用锁。
比如 Synchronized关键字来实现的锁。

Synchronized 在使用的两种形态:

  • 加在方法上
  • 加在代码块上
//加在方法上的synchronized
synchronized void demo(){
	// do something ..
}

void methodDemo(){
	//synchronized代码块
	synchronized(obj){
		//do something..
	}
}

锁的范围:(我要保护的资源的范围)

  • 实例锁,范围是在当前对象实例里面。只针对当前对象实例有效。
  • 类锁,范围是静态方法、类对象。针对所有的类对象都互斥。
public class SynchronizedDemo {
    //1 实例锁,加在方法上
    synchronized void demoMethod1(){
        System.out.println("this is demo method1");
    }
    //2 实例锁,代码块
    void demoMethod2(){
        synchronized (this){
            System.out.println("this is demo method2");
        }
    }
    //3 类锁,加在方法上
    synchronized static void demoMethod3(){
        System.out.println("this is demo method3");
    }
    //4 类所,代码块
    void demoMethod4(){
        synchronized (SynchronizedDemo.class){
            System.out.println("this is demo method4");
        }
    }

    public static void main(String[] args) {
        //实例锁生效,在同一个对象范围内互斥
        SynchronizedDemo demo = new SynchronizedDemo();
        new Thread(()->{
            demo.demoMethod1();
        },"t1").start();
        new Thread(()->{
            demo.demoMethod1();
        },"t2").start();
                
        //类锁生效,在同一个类范围互斥
        SynchronizedDemo d1 = new SynchronizedDemo();
        SynchronizedDemo d2 = new SynchronizedDemo();
        new Thread(()->{
            d1.demoMethod4();
        },"d1").start();
        new Thread(()->{
            d1.demoMethod4();
        },"d2").start();
    }
}

比如对一个共享文件的修改,当一个线程修改时,对其加锁,其他线程无法修改。
互斥锁的本质,就是对共享资源的保护,只允许一个线程对其操作。
我们来通过使用锁,对一开始的代码进行线程安全处理:

public class ThreadAtomicityDemo {
    public static int count = 0;
    public static void increase(){
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized{ThreadAtomicityDemo.class){
        	count ++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for(int i=0;i<1000;i++){
            new Thread(()->ThreadAtomicityDemo.increase()).start();
        }
        Thread.sleep(2000);
        System.out.println("The count is : " + count);
    }
}

代码执行结果:

The count is : 1000

Process finished with exit code 0

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

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

相关文章

Vue3快速上手

Vue3快速上手 1.Vue3简介 2020年9月18日&#xff0c;Vue.js发布3.0版本&#xff0c;代号&#xff1a;One Piece&#xff08;海贼王&#xff09;耗时2年多、2600次提交、30个RFC、600次PR、99位贡献者github上的tags地址&#xff1a;https://github.com/vuejs/vue-next/release…

微信上制作投票链接在线制作投票链接如果制作投票链接

现在来说&#xff0c;公司、企业、学校更多的想借助短视频推广自己。通过微信投票小程序&#xff0c;网友们就可以通过手机拍视频上传视频参加活动&#xff0c;而短视频微信投票评选活动既可以给用户发挥的空间激发参与的热情&#xff0c;又可以让商家和企业实现推广的目的&…

华为OD机试 - 知识图谱新词挖掘 1(Python)【2023-Q1 新题】

华为OD机试300题大纲 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD 清单查看地址:blog.csdn.net/hihell/category_12199275.html 华为OD详细说明:https://dream.blog.csdn.net/article/details/128980730 知识图谱新词挖掘…

利用global mapper导出等高线 在sketch up制作三维模型

为了做一个地形模型&#xff0c;绞尽脑汁实验了所有能查到的教程&#xff0c;在免费的基础上总体尝试失败&#xff0c;一是需要花钱的插件例如bitmap to mesh&#xff0c;即便能下载到&#xff0c;也是无法安装使用。如果你能下到且安装上&#xff0c;别忘了分享给我。 二是有的…

如何终止一个线程

如何终止一个线程 是使用 thread.stop() 吗&#xff1f; public class ThreadDemo extends Thread{Overridepublic void run() {try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("this is demo thread :"Thre…

学了两个月的Java,最后自己什么也不会,该怎么办?

学着学着你会发现每天的知识都在更新&#xff0c;也都在遗忘&#xff0c;可能就放弃了。但是只要自己肯练&#xff0c;肯敲代码&#xff0c;学过的知识是很容易就被捡起来的。等你学透了用不了一年也可以学好 Java的运行原理&#xff1a;Java是一门编译解释型语言&#xff0c;…

片内RAM读写测试实验

片内RAM读写测试实验 概念 RAM是FPGA中常用的基础模块&#xff0c;可广泛用于缓存数据的情况&#xff0c;同样它也是 ROM&#xff0c;FIFO 的基础。 官方已经提供了RAM的IP核进行例化即可。 读写时序&#xff08;具体还是要看官方资料&#xff09;&#xff1a; 过程 创…

华为OD机试真题 用 C++ 实现 - 获取最大软件版本号

最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…

以太网协议和DNS协议

1.以太网协议报文属性上面的图表示的是整个以太网数据报.目的地址和原地址此处的地址并非是IP地址,而是mac地址.在大小上:mac地址占有6个字节,相比于IPv4,mac可以给全球的每一台设备一个自己的mac地址.在地址的描述上:IP地址描述的是整体路程的起点和终点,而mac地址描述的是相邻…

GAN入门示例

本文参考&#xff1a;pytorch实现简单GAN - 灰信网&#xff08;软件开发博客聚合&#xff09; 上文中pytorch代码执行会有问题&#xff0c;这块本文中已经修复&#xff01; 1、GAN概述 GAN&#xff1a;Generative Adversarial Nets&#xff0c;生成对抗网络。在给定充分的建…

SpringBoot整合Mybatis+人大金仓(kingbase8)

陈老老老板&#x1f9b8;&#x1f468;‍&#x1f4bb;本文专栏&#xff1a;国产数据库-人大金仓&#xff08;kingbase8&#xff09;&#xff08;主要讲一些人大金仓数据库相关的内容&#xff09;&#x1f468;‍&#x1f4bb;本文简述&#xff1a;本文讲一下Mybatis框架整合人…

SAP MM学习笔记1-入库中的发注完了自动设定

SAP点滴学习笔记记录。 今天想整理一下MM模块儿的入库中的发注完了字段儿。 具体业务&#xff1a; 对于某个购买发注票&#xff0c;分批入库之后&#xff0c;剩下几个不想要了&#xff0c;在最后一次入库的时候&#xff0c;如何自动设定购买发注票发注完了字段。 业务流程&a…

LeetCode 160. 相交链表 -- 消除长度差

相交链表 简单 2K 相关企业 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意…

数据服务总线的搭建

关于http协议的基础知识就不介绍了。主要介绍它的报文格式。 如何显示http的报文&#xff1b; 浏览器登录服务端的IP和端口&#xff1a; 服务端接收http客户端发过来的报文&#xff1a;recv(connfd,buffer,1000,0),打印出来。 请求报文格式是请求行&#xff0c;请求头部&#…

系列四、多表查询

一、多表关系 项目开发中&#xff0c;在进行数据库表结构设计时&#xff0c;会根据业务需求及业务模块之间的关系&#xff0c;分析并设计表结 构&#xff0c;由于业务之间相互关联&#xff0c;所以各个表结构之间也存在着各种联系&#xff0c;基本上分为三种&#xff1a;一对多…

【分组CNN:超分】

Image super-resolution with an enhanced group convolutional neural network &#xff08;基于增强型分组卷积神经网络的图像超分辨率&#xff09; 具有较强学习能力的神经网络被广泛应用于超分辨率问题的求解。然而&#xff0c;CNNs依赖于更深层次的网络结构来提高图像超…

2021.3.3idea创建Maven项目

首先new - project - 找到Maven 然后按下图操作&#xff1a;先勾选使用骨架&#xff0c;再找到Maven-archetype-webapp&#xff0c;选中&#xff0c;然后next填写自己想要创建的项目名&#xff0c;然后选择自己的工作空间①、选择自己下载的Maven插件②、选择选择Maven里的sett…

基于Opencv的缺陷检测任务

数据及代码见文末 1.任务需求和环境配置 任务需求:使用opencv检测出手套上的缺陷并且进行计数 环境配置:pip install opencv-python 2.整体流程 首先,我们需要定义几个参数。 图像大小,原图像比较大,首先将图像resize一下再做后续处理图像阈值处理的相应阈值反转阈值的…

git 的使用方法(上 - 指令)

目录前言&#xff1a;一、Git 是什么&#xff1f;二、SVN与Git的最主要的区别&#xff1f;三、Git 安装四、git 配置1. 创建仓库 - repository2. 配置3. 工作流与基本操作五、Git 的使用流程1. 仓库中创建 1.txt文件2. 查看工作区的文件状态3. 添加工作区文件到暂存区4. 创建版…

c++11 之智能指针

文章目录std::shared_ptrstd::weak_ptrstd::unique_ptr智能指针多线程安全问题在实际的 c 开发中&#xff0c;我们经常会遇到诸如程序运行中突然崩溃、程序运行所用内存越来越多最终不得不重启等问题&#xff0c;这些问题往往都是内存资源管理不当造成的。比如&#xff1a; 有…