并发编程中的三大问题剖析与解决

news2024/12/26 13:11:15

java并发编程中的三大问

  • 可见性
    • 原因分析:
    • 解决:
  • 原子性
    • 原因分析:
    • 解决:
  • 有序性
    • 解决:


首先让我们来了解一下JUC

JUC(Java Util Concurrent)是Java提供的一个并发编程工具包,它解决了在多线程程序中可能遇到的各种并发问题,例如线程安全、死锁、竞态条件等。JUC提供了许多用于控制并发访问的类和接口,如ReentrantLock、Semaphore、CountDownLatch、CyclicBarrier等,这些工具可以帮助开发人员更加方便、高效地编写多线程程序,并减少由于并发问题而导致的程序错误和性能下降。

可见性

线程修改之后,其他线程也能立刻看到。
一个线程对共享变量进行修改,另一个先立即得到修改后的最新值,代码如下

 public static Boolean run = true;
    public static void main(String[] args) throws InterruptedException {
//        创建一个线程让他一直执行
        Thread t1 = new Thread(() -> {

            while (run) {
//                System.out.println("一直执行...");
            }
        });
        t1.start();

        Thread.sleep(200);

//        修改这个run的值,然后让方法一停止运行
        Thread t2 = new Thread(() -> {
            run = false;
            System.out.println("修改值完成");
        });
        t2.start();
    }

结果就是一直执行,无法停止,这是因为在这个线程中修改之后,变化的只有这个线程

原因分析:

如下就是这流程图,线程中执行完毕之后,线程保存了共享变量却无法保存在内存中。
线程图

解决:

这里我们就可以使用java中的关键字

  • voliate:每次变量修改之后,就会去将主内存中的变量去情况,还有各个线程中的变量也去清空
    这样如果这个线程再次使用的时候,就会再次去主内存中去读取这个变量的数据。
  • synchronized:这里就去定义一个对象,去设置一个相当于是锁的对象,原理与voliate相同
    public static voliate Boolean run = true;

需要注意的是:在写System.out.println(“一直执行…”);因为它的底层是有加synchronized,所以就不会存在可见性问题

原子性

原子型就是因为有一个任务必须一次性完成,中间不会被其他线程去干扰。
模拟原子操作

  1. 创建五个线程
  2. 对同一个变量进行++操作
  3. 最后查看结果,就可以确定是否是原子操作
    //        定义添加的变量
    static int num = 0;

    public static void main(String[] args) throws InterruptedException {
//        定义线程操作
        Runnable runnable = () -> {
//            自增操作
            for (int i = 0; i < 1000; i++) {
                num++;
            }
        };

//        定义一个集合去存放着五个线程,为了防止一次执行完毕
        ArrayList<Thread> runnableList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Thread t = new Thread(runnable);
            t.start();
            runnableList.add(t);
        }

        for (Thread run : runnableList) {
            run.join();
        }

        System.out.println(num);
    }

运行结果如下
运行结果
即使我们即使去释放了这个代码中的每个线程的执行,还是可能发生没有加到5000的情况

原因分析:

这个是反编译之后的结果,在图中我们可以看到画红框的四个量中,就是对这个数进行读取与写入的操作,很明显的可以看到,下面的操作并不是原子操作的,所以就有可能发生并发问题。
反编译结果

解决:

这里也使用synchronized来解决,只需要对num++这一步加锁即可

public static Object obj = new Object();
public static void main(String[] args) throws InterruptedException {
    ……
    synchronized (obj) {
        num++;
    }
    ……
}

有序性

一个线程不会因为计算机的优化,而将代码执行顺序进行调整。

这里补充一下,计算机的重排序不会是任意排序的,而是有规则的,列举几种不会重排序的情况
列如:写后读
Int a = 1;
Int b = a;
写后写
Int a = 1;
Int a = 2;
读后写
int a = 1;
Int b = 2;
Int a = 2;

总之就是对不会产生依赖的操作进行重排序,这些操作就可能被编译器和处理器重排序

下面举一个栗子

   /**
    * 开启两个压测的线程,让他们去执行
    *   第一个方法去输出值,第二个方法去赋值
    *   那么我们只需要去判断可能出现的一个情况,就能知道,是否重排
    *  情况列举:
    *       线程执行到num=2,ready是false输出:r1 = 1
    *       线程执行到rc2,方法执行完毕之后,在执行ac1输出r1 = 4
    *
    *      最后一种情况是因为指令重排才可能出现:
    *       就有可能出现ac2重排,那么就会出现下面这种情况
    *           ready = true
    *           num = 2
    *          最后因为ready = true,然后切换到ac1这个方法r1 = 0
    */
代码实现
int num = 0;
 boolean ready = false;

 @Actor
 public void actor1(I_Result r) {
     if (ready) {
         r.r1 = num + num;
     } else {
         r.r1 = 1;
     }
 }

 @Actor
 public void actor2(I_Result r) {
     num = 2;
     ready = true;
 }

最后结果分析r1 = 0也是有的

解决:

    synchronized (obj) {
        if (ready) {
            r.r1 = num + num;
        } else {
            r.r1 = 1;
        }
    }
    synchronized (obj) {
        num = 2;
        ready = true;
    }

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

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

相关文章

【Linux】进程地址空间的初步理解

目录 程序地址空间空间布局图引入物理地址与虚拟地址的概念虚拟地址空间虚拟地址与物理地址是如何对应的&#xff1f;※父子进程独立性的理解&#xff08;重点&#xff09;fork两个返回值的原理地址空间为什么要存在&#xff1f;补充理解 程序地址空间空间布局图 &#xff08;这…

IDEA 新版安装教程

目录 一、安装IDEA 1、双击安装&#xff0c;然后下一步 2、修改默认安装路径&#xff0c;自定义目录。(建议所有开发工具都放在同一个盘符) 3、改为自定义安装路径&#xff0c;下一步。&#xff08;不用使用中文或空格&#xff09; 4、创建桌面图标等 5、点击安装&#x…

02.DolphinScheduler数据源中心

文章目录 MySQLHIVE数据源使用HiveServer2使用 HiveServer2 HA Zookeeper Clickhouse MySQL 填写参数 数据源&#xff1a;选择 MYSQL数据源名称&#xff1a;输入数据源的名称描述&#xff1a;输入数据源的描述IP 主机名&#xff1a;输入连接 MySQL 的 IP端口&#xff1a;输入…

一维卷积与一维平均池化的时间复杂度

计算请参考这篇文章&#xff1a; (284条消息) 卷积神经网络的时间、空间复杂度以及数据流的变化_卷积的时间复杂度_Briwisdom的博客-CSDN博客 1. 时间复杂度 时间复杂度即模型的运行次数。 单个卷积层的时间复杂度&#xff1a;Time~O(M^2 * K^2 * Cin * Cout) //有的好奇小宝…

Spring(11. 循环依赖 - 周阳)学习笔记

上一篇 &#xff1a;10. 面试问题简析 文章目录 1. Spring AOP1.1. Aop 常用注解1.2 测试前的准备工作1.2.1 业务类1.2.2 切面类 1.3 Spring4 下的测试1.3.1 POM 文件1.3.2 创建测试类1.3.3 Aop 测试结果 1.4 Spring 5 下的测试1.4.1 POM 文件1.4.2 创建测试类1.4.3 Aop 测试结…

e签宝,「进化」在2023

精准布局生态化、统一化、智能化、信创化&#xff0c;辅以具体产品落地&#xff1b;加速产业、行业、企业、业务&#xff0c;“四业”互通互联&#xff0c;提高产业数字化渗透率。电子签“群战”时代&#xff0c;e签宝再次进化。 作者|斗斗 出品|产业家 “印章在谁手上&…

Camtasia 2023版强悍来袭,会哪些新功能呢?

Camtasia Studio 是一款专门录制屏幕动作的工具&#xff0c;它能在任何颜色模式下轻松地记录 屏幕动作&#xff0c;包括影像、音效、鼠标移动轨迹、解说声音等等&#xff0c;另外&#xff0c;它还具有即时播放和编 辑压缩的功能&#xff0c;可对视频片段进行剪接、添加转场效果…

享受简单上传体验:将Maven仓库迁移到GitHub

前言&#xff1a;我为什么放弃了Maven Central 之前我写过一篇《Android手把手&#xff0c;发布开源组件至 MavenCentral仓库》&#xff0c;文中详细介绍了如何发布组件到Maven Central中供所有开发者共用。但是最近使用下来&#xff0c;发现Sonatype JIRA 的Maven Center上传…

python接口自动化测试 requests库的基础使用

目录 简单介绍 Get请求 Post请求 其他类型请求 自定义headers和cookies SSL 证书验证 响应内容 获取header 获取cookies 简单介绍 requests库简单易用的HTTP库 Get请求 格式&#xff1a; requests.get(url) 注意&#xff1a;若需要传请求参数&#xff0c;可直接在 …

c++STL标准库排序函数std::sort使用

Qt系列文章目录 文章目录 Qt系列文章目录前言一、错误原因二、修改后的代码 前言 C sort()排序函数 C STL 标准库中的 sort() 函数&#xff0c;本质就是一个模板函数。正如表 1 中描述的&#xff0c;该函数专门用来对容器或普通数组中指定范围内的元素进行排序&#xff0c;排序…

【ML】windows 安装使用pytorch

使用pytorch需要python环境&#xff0c;建议是直接装anaconda &#xff0c;IDE用visual studio anaconda安装 Anaconda 是一个用于科学计算的 Python 发行版&#xff0c;支持 Linux, Mac, Windows, 包含了众多流行的科学计算、数据分析的 Python 包 官网链接anaconda 本人下载…

(转)maven安装及配置(详细版)

1.下载&#xff1a; 方式一可以从官方下载&#xff0c;下载页面&#xff1a;http://maven.apache.org/download.cgi 方式二&#xff1a;或者题主提供的版本下载maven安装包 提取码&#xff1a;ysns 下载好后是一个压缩文件 2.安装&#xff1a; maven压缩包解压到一个没有中文&a…

AI 编程

GitHub Copilot&#xff08;收费&#xff09; 开发者&#xff1a;微软 openAI 2022年8月22日之后开始收费&#xff0c;10美元/月&#xff0c;100美元/年。 CodeGeeX&#xff08;免费&#xff09; CodeGeeX 可以根据自然语言注释描述&#xff08;支持中英文注释&#xff09…

20.$refs

$refs是vue操作DOM用的&#xff0c;每一个vue组件实例上&#xff0c;都包含一个$refs对象&#xff0c;里面存储对应的DOM元素或组件的引用&#xff0c;默认情况下$refs对象为空 目录 1 $refs在哪 2 使用ref操作DOM 3 使用ref操作组件 3.1 使用组件方法 3.2 操作组件…

13 JS04——运算符

目标&#xff1a; 1、运算符 2、算数运算符 3、递增和递减运算符 4、比较运算符 5、逻辑运算符 6、赋值运算符 7、运算符优先级 一、运算符 1、概念 运算符&#xff08;operator&#xff09;也被称作操作符&#xff0c;是用于实现赋值、比较和执行算数运算等功能的符号。 2…

解决java普通项目读取不到resouces目录下资源文件的办法

现象如下&#xff1a; 可以看到resources目录已经在idea中标记成了资源目录resources root&#xff0c;而且target/classes目录下也编译出了resources目录下的pci.properties文件&#xff0c;换句话说&#xff1a;java在编译时是读取到了resources下的文件的。 可是为什么new F…

App性能优化方案——布局层级太多怎么优化?

作者&#xff1a;小海编码日记 View整体布局是通过深度优先的方式来进行组织的&#xff0c;整体形似一颗树&#xff0c;所以优化布局层级主要通过三个方向来实施&#xff1a; 降低布局深度&#xff1a;使用merge标签或者布局层级优化等手段来减少View树的深度&#xff1b;布局…

代码随想录算法训练营第四十三天|1049. 最后一块石头的重量 II 、494. 目标和、474.一和零

文章目录 背包问题题型1049. 最后一块石头的重量 II494. 目标和474.一和零 背包问题题型 等和子集 —0-1背包能否装满最后一块石头—0-1背包尽量装满目标和—0-1背包装满&#xff0c;且有多少种装的方式&#xff08;组合问题&#xff09; 1049. 最后一块石头的重量 II 题目链…

网页爬虫之WebPack模块化解密(JS逆向)

WebPack打包: webpack是一个基于模块化的打包&#xff08;构建&#xff09;工具, 它把一切都视作模块。 概念&#xff1a; webpack是 JavaScript 应用程序的模块打包器,可以把开发中的所有资源&#xff08;图片、js文件、css文件等&#xff09;都看成模块&#xff0c;通过loade…

Java中Lambda表达式(面向初学者)

目录 一、Lambda表达式是什么&#xff1f;什么场景下使用Lambda&#xff1f; 1.Lambda 表达式是什么 2.函数式接口是什么 第二章、怎么用Lambda 1.必须有一个函数式接口 2.省略规则 3.Lambda经常用来和匿名内部类比较 第三章、具体使用场景举例&#xff08;&#xff09; …