广告
返回顶部
首页 > 资讯 > 前端开发 > node.js >怎么理解Java诡异并发中的有序性
  • 498
分享到

怎么理解Java诡异并发中的有序性

2024-04-02 19:04:59 498人浏览 安东尼
摘要

这篇文章主要介绍“怎么理解Java诡异并发中的有序性”,在日常操作中,相信很多人在怎么理解Java诡异并发中的有序性问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么理解Ja

这篇文章主要介绍“怎么理解Java诡异并发中的有序性”,在日常操作中,相信很多人在怎么理解Java诡异并发中的有序性问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么理解Java诡异并发中的有序性”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

序、有序性的阐述

有序性为什么要探讨?因为 Java  是面向对象编程的,关注的只是最终结果,很少去研究其具体执行过程?正如上一篇文章在介绍可见性时描述的一样,操作系统为了提升性能,将 Java  语言转换成机器语言的时候,吩咐编译器对语句的执行顺序进行了一定的修改,以促使系统性能达到最优。所以在很多情况下,访问一个程序变量(对象实例字段,类静态字段和数组元素)可能会使用不同的顺序执行,而不是程序语义所指定的顺序执行。

正如大家所熟知那样,Java语言是运行在 Java 自带的 JVM(Java Virtual Machine)  环境中,在JVM环境中源代码(.class)的执行顺序与程序的执行顺序(runtime)不一致,或者程序执行顺序与编译器执行顺序不一致的情况下,我们就称程序执行过程中发生了重排序

而编译器的这种修改是自以为能保证最终运行结果!因为在单核时代完全没问题;但是随着多核时代的到来,多线程的环境下,这种优化碰上线程切换就大大的增加了事故的出现几率!

好心办了坏事!

也就是说,有序性 指的是在代码顺序结构中,我们可以直观的指定代码的执行顺序,  即从上到下按序执行。但编译器和CPU处理器会根据自己的决策,对代码的执行顺序进行重新排序。优化指令的执行顺序,提升程序的性能和执行速度,使语句执行顺序发生改变,出现重排序,但最终结果看起来没什么变化(单核)。

有序性问题  指的是在多线程环境下(多核),由于执行语句重排序后,重排序的这一部分没有一起执行完,就切换到了其它线程,导致的结果与预期不符的问题。这就是编译器的编译优化给并发编程带来的程序有序性问题。

用图示就是:

怎么理解Java诡异并发中的有序性

阿粉小结:编译优化最终导致了有序性问题。

一、导致有序性的原因:

如果一个线程写入值到字段 a,然后写入值到字段 b ,而且b的值不依赖于 a 的值,那么,处理器就能够自由的调整它们的执行顺序,而且缓冲区能够在 a  之前刷新b的值到主内存。此时就可能会出现有序性问题。

例子:

1import java.time.LocalDateTime;  2  3  8public class OrderlyDemo {  9 10    static int value = 1; 11    private static boolean flag = false; 12 13    public static void main(String[] args) throws InterruptedException { 14        for (int i = 0; i < 199; i++) { 15            value = 1; 16            flag = false; 17            Thread thread1 = new DisplayThread(); 18            Thread thread2 = new CountThread(); 19            thread1.start(); 20            thread2.start(); 21            System.out.println("========================================================="); 22            Thread.sleep(6000); 23        } 24    } 25 26    static class DisplayThread extends Thread { 27        @Override 28        public void run() { 29            System.out.println(Thread.currentThread().getName() + " DisplayThread begin, time:" + LocalDateTime.now()); 30            value = 1024; 31            System.out.println(Thread.currentThread().getName() + " change flag, time:" + LocalDateTime.now()); 32            flag = true; 33            System.out.println(Thread.currentThread().getName() + " DisplayThread end, time:" + LocalDateTime.now()); 34        } 35    } 36 37    static class CountThread extends Thread { 38        @Override 39        public void run() { 40            if (flag) { 41                System.out.println(Thread.currentThread().getName() + " value的值是:" + value + ", time:" + LocalDateTime.now()); 42                System.out.println(Thread.currentThread().getName() + " CountThread flag is true,  time:" + LocalDateTime.now()); 43            } else { 44                System.out.println(Thread.currentThread().getName() + " value的值是:" + value + ", time:" + LocalDateTime.now()); 45                System.out.println(Thread.currentThread().getName() + " CountThread flag is false, time:" + LocalDateTime.now()); 46            } 47        } 48    } 49}

运行结果:

怎么理解Java诡异并发中的有序性

从打印的可以看出:在 DisplayThread 线程执行的时候肯定是发生了重排序,导致先为 flag 赋值,然后切换到 CountThread  线程,这才出现了打印的 value 值是1,falg 值是 true 的情况,再为 value  赋值;不过出现这种情况的原因就是这两个赋值语句之间没有联系,所以编译器在进行代码编译的时候就可能进行指令重排序。

用图示,则为:

怎么理解Java诡异并发中的有序性

二、如何解决有序性

2.1、volatile

volatile 的底层是使用内存屏障来保证有序性的(让一个 CPU 缓存中的状态(变量)对其他 CPU  缓存可见的一种技术)。

volatile 变量有条规则是指对一个 volatile 变量的写操作, Happens-Before于后续对这个 volatile  变量的读操作。并且这个规则具有传递性,也就是说:

怎么理解Java诡异并发中的有序性

此时,我们定义变量 flag 时使用 volatile 关键字修饰,如:

1    private static volatile boolean flag = false;

此时,变量的含义是这样子的:

怎么理解Java诡异并发中的有序性

也就是说,只要读取到 flag=true; 就能读取到 value=1024;否则就是读取到flag=false; 和 value=1  的还没被修改过的初始状态;

怎么理解Java诡异并发中的有序性

但也有可能会出现线程切换带来的原子性问题,就是读取到 flag=false; 而value=1024  的情况;看过上一篇讲述[原子性]()的文章的小伙伴,可能就立马明白了,这是线程切换导致的。

怎么理解Java诡异并发中的有序性

2.2、加锁

此处我们直接采用Java语言内置的关键字  synchronized,为可能会重排序的部分加,让其在宏观上或者说执行结果上看起来没有发生重排序。

代码修改也很简单,只需用 synchronized 关键字修饰 run 方法即可,代码如下:

1    public synchronized void run() { 2        value = 1024; 3        flag = true; 4    }

同理,既然是加锁,当然也可以使用 Lock 加锁,但 Lock  必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。这点在使用的时候一定要注意!

使用该种方式加锁也很简单,代码如下:

1    readWriteLock.writeLock().lock(); 2    try { 3        value = 1024; 4        flag = true; 5    } finally { 6        readWriteLock.writeLock().unlock(); 7    }

到此,关于“怎么理解Java诡异并发中的有序性”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

--结束END--

本文标题: 怎么理解Java诡异并发中的有序性

本文链接: https://www.lsjlt.com/news/85197.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

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

下载Word文档
猜你喜欢
  • 怎么理解Java诡异并发中的有序性
    这篇文章主要介绍“怎么理解Java诡异并发中的有序性”,在日常操作中,相信很多人在怎么理解Java诡异并发中的有序性问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么理解Ja...
    99+
    2022-10-19
  • 怎么理解Java并发可见性
    本篇内容介绍了“怎么理解Java并发可见性”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!01 可见性的阐述可见性 的定义是:一个线程对共享变...
    99+
    2023-06-16
  • Java的并发锁怎么理解
    本篇内容主要讲解“Java的并发锁怎么理解”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java的并发锁怎么理解”吧!   Java 中的并发锁大致分为隐式锁...
    99+
    2022-10-19
  • 怎么深入理解Java多线程与并发框中的顺序一致性模型
    怎么深入理解Java多线程与并发框中的顺序一致性模型,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。一、竞态条件(Race Condition)计算的正确性取决于 多个线程 执行...
    99+
    2023-06-05
  • 如何理解java 并发中的原子性与可视性
    如何理解java 并发中的原子性与可视性?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。java 并发中的原子性与可视性实例详解并发其实是一种解耦合的策略,它帮助我们把做什么...
    99+
    2023-05-31
    java 并发 原子性
  • 解密Java中HTTP并发处理和JavaScript的异同之处
    Java和JavaScript是两种不同的编程语言,但它们在Web开发中都起着重要的作用。其中,Java在Web服务器端广泛应用于HTTP并发处理,而JavaScript则主要用于前端开发。本文将,通过演示代码帮助读者更好地理解它们的区别和...
    99+
    2023-09-06
    http 并发 javascript
  • 深入理解Java多线程与并发框(第③篇)——Java内存模型与原子性、可见性、有序性
    一、Java内存模型Java Memory Modle,简称 JMM,中文名称 Java内存模型,它是一个抽象的概念,用来描述或者规范访问内存变量的方式。因为各中计算机的操作系统和硬件不同,方式机制也可能不同,Java内存模型用于屏蔽(适配...
    99+
    2023-06-05
  • java多线程中的并发工具类CountDownLatch,CyclicBarrier和Semaphore该怎么理解
    本篇文章给大家分享的是有关java多线程中的并发工具类CountDownLatch,CyclicBarrier和Semaphore该怎么理解,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起...
    99+
    2023-06-22
  • 怎么理解java高并发的用户线程和守护线程
    这篇文章主要讲解了“怎么理解java高并发的用户线程和守护线程”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么理解java高并发的用户线程和守护线程”吧!守护线程是一种特殊的线程,在后台默...
    99+
    2023-06-25
  • Java异步事件的轮询与中断怎么理解
    这篇文章主要讲解了“Java异步事件的轮询与中断怎么理解”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java异步事件的轮询与中断怎么理解”吧!CPU几乎把所有的时间都花费在从内存获取指令并...
    99+
    2023-06-17
  • 怎样深入理解Java多线程与并发框中的synchronized 关键字
    怎样深入理解Java多线程与并发框中的synchronized 关键字,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。一、Class文件与对象对象头 32位JVM的对象头二、sy...
    99+
    2023-06-05
  • Java异常处理中怎么写出“正确”但被编译器认为有语法错误的程序
    Java异常处理中怎么写出“正确”但被编译器认为有语法错误的程序,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。文章的标题看似自相矛盾,然而我在“正确”二字上打了...
    99+
    2023-06-02
  • java对象实例化过程中的多态特性怎么理解
    本篇内容主要讲解“java对象实例化过程中的多态特性怎么理解”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“java对象实例化过程中的多态特性怎么理解”吧!java 对象实例化过程中的多态特性执行...
    99+
    2023-06-21
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作