并发编程- synchronized,Lock及volatile的使用

news2025/7/19 10:13:38

文章目录

    • 并发编程的可见性问题
    • 解决方法
      • synchronized
      • Lock
      • volatile

并发编程的可见性问题

多线程访问共享变量,造成线程不安全,最后的数值不对


public class VDemo {

    private static int num = 0;

    public static void add() {
        num++;
    }

    public static void main(String[] args) {
        //理论上应该为20000
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 1; j <= 1000; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println("执行结果:"+num);
    }
}

执行结果:13696

为什么出现这种问题?

是由于多个线程访问同一个变量,第一个值为0,加了1,第二个又迅速的访问,发现读取到的值依旧是0,没有获取到最新值,导致读取到的永远是旧值,这就造成了 这种问题,正常的数字应该是20000,然而少了很多。

解决方法

synchronized

public class VDemo {

    private static int num = 0;

    public static void add() {
        num++;
    }

    public static void main(String[] args) {
        //理论上应该为20000
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                //加锁来保证原子性,锁的是当前线程.class
                synchronized (Thread.class) {
                    for (int j = 1; j <= 1000; j++) {
                   		add();
                	}
                }
            }).start();
        }

        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println("执行结果:"+num);
    }
}

执行结果:20000

Lock

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class VDemo {

    private static int num = 0;

    public static void add() {
        num++;
    }

	// Lock 锁,默认为非公平锁
    private static Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        //理论上应该为20000
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                try {
                	//上锁,注意,再此必须try/catch 防止程序出现异常,从而造成死锁问题
                    lock.lock();
                    for (int j = 1; j <= 1000; j++) {
                        add();
                    }
                } catch (Exception e) {

                } finally {
                	// 解锁
                    lock.unlock();
                }
            }).start();
        }

        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println("执行结果:"+num);
    }
}

执行结果:20000

volatile

import java.util.concurrent.atomic.AtomicInteger;

public class VDemo {

    //原子类的 AtumicInteger
    private volatile static AtomicInteger num = new AtomicInteger();

    public static void add() {
        //+1 方法	底层使用CAS
        num.getAndIncrement(); 
    }

    public static void main(String[] args) {
        //理论上应该为20000
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 1; j <= 1000; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println("执行结果:"+num);
    }
}

执行结果:20000

getAndIncrement()方法:

在这里插入图片描述
底层使用了CAS

底层和操作系统挂钩,在内存中修改值 Unsafe 是很特殊的一个类

指令重排

什么是指令重排:你写的程序计算机并不是按照你写的那样去执行的

源代码 --> 编译器优化重排 —> 指令并行也可能会重排 --> 内存系统也可能会重排 —> 执行

处理器在执行指令重排的时候,考虑:数据之间的依赖性

int a = 1;	// 1 
int b = 2;	// 2
a = a + 2;  // 3
b = a * a;  // 4

//执行顺序:1234,通过指令重排后,可能是 2134, 1324

在多线程的情况下指令重排可能会导致结果不一致

volatile可以避免指令重排

内存屏障,CPU指令 ,作用

  1. 保证特定的操作的执行顺序
  2. 可以保证某些变量的内存可见性(利用这些特性volatile实现了可见性)

volatile 读 / 写插入内存屏障规则:

  • 在每个 volatile 读操作的后面插入 LoadLoad 屏障和 LoadStore 屏障。
  • 在每个 volatile 写操作的前后分别插入一个 StoreStore 屏障和一个 StoreLoad 屏障

编辑器不会对带有修饰volatile的属性产生指令重排

Volatile 是可以保证可见性 不能保证原子性,由于内存屏障,可以避免指令重排的现象产生!

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

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

相关文章

红队内网渗透神器--CobaltStrike安装教程

CobaltStrike介绍&#xff1a; CobaltStrike是一款渗透测试神器&#xff0c;被业界人称为CS神器。CobaltStrike分为客户端与服务端&#xff0c;服务端是一个&#xff0c;客户端可以有多个&#xff0c;可被团队进行分布式协团操作。 CobaltStrike集成了端口转发、服务扫描&…

Ubuntu 手动配置DNS

使用ping命令测试百度域名时发现&#xff0c;无法解析这个域名&#xff0c;说明当前系统上没有配置DNS服务器。配置DNS服务器的方式主要有以下两种&#xff1a; 目录 1、修改DNS配置文件 /etc/resolv.conf 2、修改网卡配置文件 /etc/network/interfaces 1、修改DNS配置文件 /e…

【附源码】计算机毕业设计JAVA宠物云寄养系统

【附源码】计算机毕业设计JAVA宠物云寄养系统 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JAVA myba…

Vue3 - toRef() 使用教程

介绍 它可用于为响应式对象上的 property 创建 ref&#xff0c;这样创建的 ref 与其源 property 保持同步&#xff0c;当改变源 property 时&#xff0c;将更新 ref &#xff0c;反之亦然。 这段话有些晦涩难懂&#xff0c;其实用大白话说&#xff0c;就是当你使用 reactive 创…

如何拆分PDF成单页?这三个方法分享给你

很多朋友在平时的工作中&#xff0c;经常需要处理一些PDF格式的文件&#xff0c;但是如果PDF文件的占用空间太大&#xff0c;难以进行操作处理&#xff0c;这时我们就需要先将其拆分成多个小文件&#xff0c;那你们知道要怎么把PDF拆分成多个文件吗&#xff1f;今天我就来给大家…

RK3568平台开发系列讲解(LCD篇)DRM 显示框架

🚀返回专栏总目录 文章目录 一、DRM 显示框架二、DRM 驱动和 libdrm 交互过程2.1、GEM:2.2、KMS:三、DRM 驱动路径3.1、Uboot驱动路径3.2、内核驱动路径沉淀、分享、成长,让自己和他人都能有所收获!😄 📢DRM 英文名叫 Direct Rendering Manager,用来管理显示输出,图…

App Languages 批量导入管理flutter多语言文案

前段时间AppLanguages推出了iOS、Mac版的多语言文案导入功能&#xff0c;好几个小伙伴点赞&#xff0c;称其为“干货工具”&#xff0c;最近加班加点支持了flutter的多语言文案管理功能。 操作界面 批量导入 1&#xff09;需要选择lib文件夹的路径&#xff0c;方便创建和寻找…

2022年11月华南师范大学自考本科-计算机信息管理课程实验—《数据库系统原理》实践题目

《 计算机信息管理课程实验——数据库系统原理 》课程试卷 答卷提交说明&#xff1a; 在mysql环境下填写SQL命令完成以下实践的题目&#xff0c;并返回执行结果的截图&#xff0c;答卷的答题格式如下&#xff0c;包括三部分&#xff1a;题目&#xff0c;SQL文本代码&#x…

【测试开发面试】6家企业真实面试,最终成功入职外企......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 粉丝小A 测试开发的…

3、HTML——注释、转义字符、超链接标签、锚链接、功能性超链接、列表标签、有序列表、无序列表、定义列表

目录 一、注释标签 二、转义字符 1、空格&#xff1a; 2、大于号/小于号&#xff1a;>/< 3、引号&#xff1a;" 4、版权&#xff1a;© 5、商标&#xff1a;& 6、常见转义字符 三、超链接标签&#xff1a;a 四、锚链接 1、跳转同网页位置 2、…

铁威马NAS如何开启二次验证提高系统安全性

想到登录TNAS时更安全&#xff1f;直接开启OTP二次验证&#xff0c;通过 TNAS mobile生成的一次性密码登录NAS存储&#xff0c;简单设置&#xff0c;提升TOS系统访问安全性给你TNAS双重保护。 1.首先&#xff0c;确认你的TOS系统在5.0.176以上&#xff1b; 2.登录TOS 系统后&…

中国为什么要发展人工智能

“基建狂魔”,这是很多网友对中国的爱称。一方面是知道中国的基础设施建设速度很快,另一方面也是对中国整体实力的信心。疫情时期,武汉方舱医院只用了10多天就完成了建造,震惊世界,也让国人骄傲。 如果只看现在,你可能会觉得中国有这样的速度,是理所当然。但如果你知道最…

FPGA SATA IP控制器的SATA接口调试记录

本文档是基于FPGA K7 SATA IP控制器的SATA接口调试记录&#xff0c;接口遵循标准的ACHI协议。 操作系统内核版本&#xff1a;5.4.18 由于K7PCIE只有3个bar&#xff0c;AHCI协议规定SATA控制器是在第四个BAR上&#xff0c;另外由于PCIE配置空间设备类寄存器和能力寄存器未配置成…

数学之美系列 1.3w字精简版阅读笔记

目录 系列一&#xff1a;统计语言模型 (Statistical Language Models) 系列二&#xff1a;谈谈中文分词 系列三&#xff1a;隐含马尔可夫模型在语言处理中的应用 系列四&#xff1a;怎样度量信息 系列五&#xff1a;布尔代数与搜索引擎的索引 系列六&#xff1a;图论和网…

【深入理解Kotlin协程】协程中的Channel和Flow 协程中的线程安全问题

热数据通道 Channel Channel 实际上就是 个并发安全的队列&#xff0c;它可以用来连接协程&#xff0c;实现不同协程的通信&#xff0c;代码如代码清单所示 suspend fun testChannel() {val channel Channel<Int>() var i 0//生产者 发val producer GlobalScope.lau…

双立方插值原理分析

双立方插值原理分析双立方插值 : 其核心思想是利用三次多项式S(x)求逼近理论上最佳插值函数sin(x)/x&#xff0c;待求像素(x, y)的灰度值由其周围16个灰度值加权内插得到公式推导过程 上述图片中像素点的说明&#xff1a; 红色的点为16个真实存在的像素点 P 为双立方插值待插…

SCD1: 微服务概览

1.1简介 1.1.1 概念 集群&#xff1a; 它是一种物理形态&#xff0c;简单来讲就是把同一个业务部署到多个服务器上。而在接收到请求流量时&#xff0c;主要是通过负载均衡器&#xff0c;来进行流量分配。   分布式&#xff1a; 它是一种工作方式&#xff0c;将一个业务拆成多…

SpringMVC学习篇(三)

转发和重定向 1 保存数据四种方式 1.1 转发范围 1.1.1 Model a 设置值 a.1 语法 Model对象.addAttribute(key,value);a.2 示例 m.addAttribute("a","香蕉");b 有效范围 在转发的当前页面有效1.1.2 ModelMap a 设置值 a.1 语法 ModelMap对象.addAt…

API网关功能一览

API网关功能一览 无论是单体应用&#xff0c;SOA或者现在流行的微服务架构&#xff0c;都需要一个API网关。 API网关作为内网的入口&#xff1b;统一对外提供服务。 一些公共服务的建设&#xff0c;也可以在网关层统一处理。 网关应该是无状态&#xff0c;容易线性扩展的;运行时…

基于Swagger的接口自动化测试

本文是一篇讲述敦煌网云原生微服务与服务接口(API)自动化测试实现的文章。 云原生微服务框架项目升级在如火如荼的进行中&#xff0c;根据磐石框架延伸产生的测试技术&#xff0c;亦是本文主旨所在。 一、Swagger介绍 原始时代&#xff0c;可能在工程开发前夕&#xff0c;会…