文章详情

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

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-11-30 00:24

关注

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 用来保存结果。

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

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

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
                                
                            
                        
                    
                
            
        
    


来源:springboot葵花宝典内容投诉

免责声明:

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

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

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯