我的编程空间,编程开发者的网络收藏夹
学习永远不晚

深入理解并发编程中的三个问题

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

深入理解并发编程中的三个问题

1.1 可见性案例演示

一个线程根据boolean类型的标记flag, while循环,另一个线程改变这个flag变量的值,另 一个线程并不会停止循环。

public class VisibilityTest {
    // 多个线程都会访问的数据,我们称为线程的共享数据
    // 定义一个静态的 boolean 变量 run,初始值为 true
    private static boolean run = true; 

    public static void main(String[] args) throws InterruptedException {
        // 创建并启动线程 t1
        Thread t1 = new Thread(() -> {
            // 在 run 变量为 true 时循环输出消息
            while (run) {
                System.out.println(Thread.currentThread().getName() + "线程1 run = " + run);
            }
            // 循环结束后输出最终消息
            System.out.println(Thread.currentThread().getName() + "线程1 run = " + run);
        });
        t1.start(); // 启动线程 t1

        Thread.sleep(1000); // 主线程睡眠1秒钟

        // 创建并启动线程 t2
        Thread t2 = new Thread(() -> {
            run = false; // 将 run 变量设置为 false
            System.out.println(Thread.currentThread().getName() + "时间到,线程2设置为false");
        });
        t2.start(); // 启动线程 t2
    }
}

输出结果:

Thread-0线程1 run = true
Thread-0线程1 run = true
// .....结果省略
Thread-1时间到,线程2设置为false
Thread-0线程1 run = false

线程1开始运行时run=true,所以会不断循环输出Thread-0线程1 run = true,线程2运行后设置run=false,线程1发现run=true后就停止输出。

总结:并发编程时,会出现可见性问题,当一个线程对共享变量进行了修改,另外的线程并没有立即看到修改后的最新值。

2.原子性

原子性(Atomicity):在一次或多次操作中,要么所有的操作都执行并且不会受其他因素干扰而中断,要么所有的操作都不执行。

2.1 可见性案例演示

5个线程各执行1000次 i++

public class AtomicityTest {
    private static int number = 0; // 定义一个静态的整数变量 number,初始值为 0

    public static void main(String[] args) throws InterruptedException {
        Runnable increment = () -> {
            // 定义一个 Runnable 匿名类 increment,用于对 number 进行累加操作
            for (int i = 0; i < 1000; i++) {
                number++; // 对 number 进行累加操作
            }
        };

        ArrayList ts = new ArrayList<>(); // 创建一个 ArrayList 用于存储线程对象
        for (int i = 0; i < 5; i++) {
            Thread t = new Thread(increment); // 创建一个新线程,传入 increment Runnable 实例
            t.start(); // 启动线程
            ts.add(t); // 将线程对象添加到 ArrayList 中
        }

        for (Thread t : ts) {
            t.join(); // 等待所有子线程执行完毕
        }

        System.out.println("number = " + number); // 输出最终的 number 值
    }
}

思考:最终number结果可能是多少?

结果可能是5000,也可能是小于5000的结果都有可能。

对于 number++ 而言(number 为静态变量),实际会产生如下的 JVM 字节码指令:

9: getstatic #12 // Field number:I
12: iconst_1
13: iadd
14: putstatic #12 // Field number:I

可见number++是由多条语句组成,以上多条指令在一个线程的情况下是不会出问题的,但是在多线程情况下就可能会出现问题。比如一个线程在执行13: iadd时,另一个线程又执行9: getstatic。会导致两次number++,实际上只加了1。

总结:并发编程时,会出现原子性问题,当一个线程对共享变量操作到一半时,另外的线程也有可能来操作共享变量,干扰了前一个线程的操作。

3.有序性

有序性(Ordering):是指程序中代码的执行顺序,Java在编译时和运行时会对代码进行优化,会导致程序最终的执行顺序不一定就是我们编写代码时的顺序。

3.1.  有序性可见性案例演示

有序性

jcstress是Java并发压测工具。

pom文件,添加依赖。


    org.openjdk.jcstress
    jcstress-core
    0.5

代码:

@JCStressTest
@Outcome(id = {"1", "4"}, expect = Expect.ACCEPTABLE, desc = "ok")
@Outcome(id = "0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "danger")
@State
public class OrderingTest {

   int num = 0;
   boolean ready = false;
   // 线程1执行的代码
    @Actor
   public void actor1(I_Result r) {
       if (ready) {
           r.r1 = num + num;
       } else {
           r.r1 = 1;
       }
   }

   // 线程2执行的代码
    @Actor
   public void actor2(I_Result r) {
       num = 2;
       ready = true;
   }
}

I_Result 是一个对象,有一个属性 r1 用来保存结果。

思考:在多线程情况下可能出现几种结果?

  • 情况1:线程1先执行actor1,这时ready = false,所以进入else分支结果为1。
  • 情况2:线程2执行到actor2,执行了num = 2;和ready = true,线程1执行,这回进入 if 分支,结果为 4。
  • 情况3:线程2先执行actor2,只执行num = 2;但没来得及执行 ready = true,线程1执行,还是进入else分支,结果为1。

注意:还有第四种情况,结果为0。

  • 情况4:线程2java编译后结果
ready = true;
num = 2;

线程2先执行actor2,执行了ready = true,但没来得及执行执行num = 2,线程1执行,还是进入if分支,结果为0。

运行测试:

mvn clean install
java -jar target/jcstress.jar

部分jcstress测试结果,0结果出现1384次。

完整pox.xml:


        8
        8
        UTF-8
        1.8
        jcstress
    

    
        
            org.openjdk.jcstress
            jcstress-core
            0.5
        
    


    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.1
                
                    ${javac.target}
                    ${javac.target}
                    ${javac.target}
                
            

            
                org.apache.maven.plugins
                maven-shade-plugin
                2.2
                
                    
                        main
                        package
                        
                            shade
                        
                        
                            ${uberjar.name}
                            
                                
                                    org.openjdk.jcstress.Main
                                
                                
                                    META-INF/TestList
                                
                            
                        
                    
                
            
        
    


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

深入理解并发编程中的三个问题

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

深入理解并发编程中的三个问题

一个线程根据boolean类型的标记flag, while循环,另一个线程改变这个flag变量的值,另 一个线程并不会停止循环。
线程开发2024-11-30

并发编程之ThreadLocal深入理解

在日常的开发中,我们经常会遇到在当前运行线程中保存一些信息,并且各线程之间是隔离的,不会相互影响,不存在并发问题,通过这样的方式来实现请求调用链中方法之间参数传递的解耦,提升代码结构的稳定性等。Java ThreadLocal就是用于实现这

并发编程之Synchronized深入理解

在并发编程中存在线程安全问题,主要原因有:1.存在共享数据 2.多线程共同操作共享数据。关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见(可见
Synchronized2024-12-03

Java 内存模型与死锁:深入理解并发编程中的死锁问题

本文深入探讨 Java 内存模型与死锁问题之间的关联,并以示例代码阐释死锁的成因和解决方法,旨在帮助读者深入理解并发编程中的死锁问题。
Java 内存模型与死锁:深入理解并发编程中的死锁问题
2024-02-04

深入理解Java并发编程之ThreadLocal

本文主要介绍了Java并发编程之ThreadLocal,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2022-11-13

学好并发编程,关键是要理解这三个核心问题!

分工比较官方的解释为:分工就是将一个比较大的任务,拆分成多个大小合适的任务,交给合适的线程去完成,强调的是性能。
编程高并发2024-12-03

并发编程的三大核心问题

本文对分工问题,同步问题,互斥问题这三大核心问题进行简单的介绍。
并发编程2024-12-01

深入理解Go语言的并发编程:Go的并发模型解析

Go语言作为一门流行的编程语言,以其出色的并发编程能力而闻名。并发编程是在同一时间内执行多个独立的任务,通过充分利用多核处理器的性能以提高程序的性能和效率。在Go语言中,并发编程是一种非常简单、直观和高效的方式来编写并行程序。本文将深入探讨
深入理解Go语言的并发编程:Go的并发模型解析
2024-03-04

深入理解并发编程同步工具类

要实现这个需求,我们就要思考一下看有没有一种机制能让主线程等待其他子线程完成任务后,它再继续执行它余下的业务逻辑?

C++多线程编程中的并发问题解析

C++多线程编程中的并发问题解析随着计算机硬件的不断发展,多核处理器已经成为了主流。在这种情况下,使用多线程来充分利用多核处理器的性能,成为了程序开发中的一项重要技术。然而,在多线程编程中,由于多个线程之间的并发操作,常常会导致一些问题,这
2023-10-22

深入解析Golang的并发编程模型

Golang作为一种开发高效、简洁的编程语言,具有非常强大的并发编程能力,为开发者提供了丰富的工具和机制来处理并发问题。本文将深入解析Golang的并发编程模型,包括Goroutine、Channel、互斥锁等机制,并通过具体的代码示例展示
深入解析Golang的并发编程模型
2024-03-01

深入了解golang中的Select Channels Go并发式编程

在Go语言中,可以使用select语句来进行通道的选择操作,实现并发式编程。select语句用于监听多个通道的操作,并在其中一个通道可操作时执行相应的代码块。select语句的基本语法如下:goselect {case // 执行 chan
2023-10-20

深入了解并实践golang中的Select Channels Go并发式编程

在Go语言中,可以使用select语句和通道(Channel)来进行并发式编程。通道(Channel)是用来在Go协程之间进行通信的一种方式。通过通道,可以安全地传递数据,实现协程之间的同步。在通道中发送数据使用`select语句用于处理多
2023-10-08

深入分析java并发编程中volatile的实现原理

引言在多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能
2023-05-30

大规模数据处理中的Python并发编程问题详解

大规模数据处理中的Python并发编程问题详解在当今数据爆炸的时代,大规模数据处理成为了许多领域的重要任务。对于处理海量数据,提高处理效率是至关重要的。而在Python中,通过并发编程可以有效提高程序的执行速度,从而更高效地处理大规模数据。
2023-10-22

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录