服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - 编程技术 - 深入理解并发编程中的三个问题

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

2024-03-19 14:43springboot葵花宝典 编程技术

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

1.可见性

可见性(Visibility):是指一个线程对共享变量进行修改,另一个先立即得到修改后的最新值

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<Thread> 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文件,添加依赖

<dependency>
    <groupId>org.openjdk.jcstress</groupId>
    <artifactId>jcstress-core</artifactId>
    <version>0.5</version>
</dependency>

代码

@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

<properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <javac.target>1.8</javac.target>
        <uberjar.name>jcstress</uberjar.name>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.openjdk.jcstress</groupId>
            <artifactId>jcstress-core</artifactId>
            <version>0.5</version>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <compilerVersion>${javac.target}</compilerVersion>
                    <source>${javac.target}</source>
                    <target>${javac.target}</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.2</version>
                <executions>
                    <execution>
                        <id>main</id>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <finalName>${uberjar.name}</finalName>
                            <transformers>
                                <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>org.openjdk.jcstress.Main</mainClass>
                                </transformer>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/TestList</resource>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>


原文地址:https://mp.weixin.qq.com/s?__biz=MzIzMjIyNTYwNg==&mid=2247489621&idx=1&sn=b300a2c19d2db094c1e9ed98e8352908

延伸 · 阅读

精彩推荐
  • 编程技术高并发是一种架构思维模式

    高并发是一种架构思维模式

    本文从浅到深依次讲述了性能是实现高并发的基础条件,控制是实现资源最大化利用的方式,以及如何通过取舍来换取当前应用系统更所需的能力,但这些...

    技术奇妙物语11862022-03-01
  • 编程技术2021年程序员必备的9项技能

    2021年程序员必备的9项技能

    和其他行业一样,软件研发行业也有必须要掌握的工具,每个程序员只有学习了这些工具之后才会不断成长,今天就和大家分享一些程序员必备的十项技能...

    程序员书库4062021-04-13
  • 编程技术滑动验证码的设计与理解

    滑动验证码的设计与理解

    这篇文章主要介绍了滑动验证码的设计与理解,本文通过实例代码给大家介绍小网站是怎么设计的,代码简单易懂,需要的朋友可以参考下...

    半天想不出昵称的斌3302020-09-22
  • 编程技术盘点儿童智力开发的首选编程语言—Scratch

    盘点儿童智力开发的首选编程语言—Scratch

    Scratch是儿童智力开发的首选编程语言,它把编程变得简单,而且可以提升孩子们 的想象力,如果可以做到亲子互动的话那么将更加有趣。 ...

    IT共享之家5662021-08-16
  • 编程技术十分钟了解Vite如何支持React

    十分钟了解Vite如何支持React

    vite 是基于浏览器支持 ESM 模块,用以解决大型应用本地开发环境打包、热更新时间久的一套解决方案,目前已支持vue、react、Svelte、Solid等主流框架,相信...

    微医大前端技术8802021-07-29
  • 编程技术2022可视化网页生成工具盘点

    2022可视化网页生成工具盘点

    面对各种网站生成器,很多人会觉得眼花缭乱,不知道选择哪一种,其实有时间你可以下载下来体验一下,每个软件花费个半小时就能体验到它的精髓。...

    梦回故里归来3742022-03-07
  • 编程技术什么是大数据系统存储及管理?

    什么是大数据系统存储及管理?

    什么是大数据系统存储及管理?根据数据存储和管理的内容范围,大数据存储及管理技术需要重点研究如何解决大数据的可存储、可表示、可处理、可靠性...

    博学谷2112020-12-02
  • 编程技术使用微信助手搭建微信返利机器人流程

    使用微信助手搭建微信返利机器人流程

    这篇文章主要介绍了使用微信助手搭建微信返利机器人流程本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下...

    指间人生6382020-09-17