热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

线程可见性和原子性,java可见性原子性

线程可见性和原子性,java可见性原子性00-10101.原子性问题2。能见度问题3。订单问题汇总问题:1.什么是原子性、可见性和有序性?00-1010原子性、可见性和有序性是并发

  线程可见性和原子性,java 可见性 原子性

  00-1010 1.原子性问题2。能见度问题3。订单问题汇总问题:

  1.什么是原子性、可见性和有序性?

  00-1010原子性、可见性和有序性是并发编程面临的三大问题。

  所谓原子操作是“一个或一系列不能中断的操作”,指的是不会被线程调度机制中断的操作。这个操作一旦开始,就会一直运行到最后,中间没有任何线程切换。

  例如,对于I,将实际生成以下JVM字节码指令:

  Getstatic i //获取静态变量I的值(内存值)iconst_1 //准备常数1 addr//自动增量(寄存器增量1)putstatic i //将修改后的值存储在静态变量I中(将值存储在内存中)。如果是单线程或更多线程,8行代码按顺序执行(无交错)。没问题:

  但是,在多线程下,这8行代码可能会交替运行:

  出现负数的情况:

  出现正数的情况:

  自增运算符是一种复合运算。这三个JVM指令,即“从内存中获取值”、“将寄存器递增1”和“将值保存到内存中”,是不可分的。它们是原子的、线程安全的,也称为原子操作。然而,当两个或多个原子操作被组合时,它们就不再是原子的了。比如写前读,有可能读后,其实这个变量被修改了,导致读写数据不一致。

  因为这四个操作可以线程切换,或者被其他线程中断。所以操作不是原子操作,原子性问题会出现在并行场景中。

  00-1010当一个线程修改共享变量时,另一个线程可以立即看到。我们称之为共享变量内存可见。

  说到内存可见性,首先要介绍Java内存模型的概念。JMM规定所有变量都应该存储在公共主存中。当线程使用变量时,它会将主存中的变量复制到自己的工作内存(私有内存)中。线程对变量的读写操作是变量在自己工作内存中的拷贝。

  如果两个线程同时操作一个共享变量,就可能发生可见性问题:

  (1)主存中有一个变量sum,初始值sum=0;

  (2)线程A计划给sum加1,先把sum=0复制到自己的私有内存中,然后更新sum的值。线程A的操作完成后,其私有内存中的和等于1,但线程A将更新后的和刷回主存的时间不固定;

  (3)就在线程A没有把sum刷回主存之前,线程B也从主存读取sum,此时值为0,用线程A进行同样的操作,但没有达到sum=2的预期目标,最终sum=1;

  线程A和线程B并发操作sum发生内存可见性问题:

  为了解决多线程的内存可见性问题,所有线程都必须将共享变量刷新到主存中。一个简单的解决方案是使用Java提供的关键字volatile来修饰共享变量。

  为什么Java局部变量和方法参数没有内存可见性问题?

  在Java中,所有的局部变量和方法定义参数不在线程间共享,所以不会有内存可见性问题。的所有对象实例、类实例和数组元素都存储在JVM堆内存中,堆内存在线程间共享,因此存在可见性问题。

  00-1010程序的有序性是指程序按照代码的顺序执行。如果程序执行的顺序与代码的顺序不同,导致错误的结果,那么就出现了顺序问题。

  @ SLF 4j public class Test3 { private static volatile int x=0,y=0;私有静态int a=0,b=0;公共静态void main(String[] args)引发interrupted exception { for(int I=0;I){ a=0;b=0;x=0;y=0;线程t1=新线程(()-{ a=1;x=b;});线程t2=新线程(()- {

   b = 1; y = a; }); t1.start(); t2.start(); t1.join(); t2.join(); // 假如t1线程先执行,t2线程后执行,则结果为a=1,x=0,b=1,y=1 (0,1) // 假如t2线程先执行,t1线程后执行,则结果为b=1,y=0,a=1,x=1 (1,0) // 假如t1线程和t2线程的指令是同时或交替执行的,则结果为a=1,b=1,x=1,y=1 (1,1) // 但是不可能出现(0,0) if(x==0 && y==0){ log.debug("x:{}, y:{}",x,y); } } }}由于并发执行的无序性,赋值之后x、y的值可能为(1,0)、(0,1)或(1,1)。为什么呢?因为线程t1可能在线程t2开始之前就执行完了,也可能线程t2在线程t1开始之前就执行完了,甚至有可能二者的指令是同时或交替执行的。

  然而,执行以上代码时,出乎意料的事情发生了:这段代码的执行结果也可能是(0,0),部分结果如下:

  

19:37:32.113 [main] DEBUG com.example.test.Test3 - x:0, y:019:37:33.041 [main] DEBUG com.example.test.Test3 - x:0, y:019:37:34.501 [main] DEBUG com.example.test.Test3 - x:0, y:019:37:41.825 [main] DEBUG com.example.test.Test3 - x:0, y:0

 

  

于以上程序来说,(0,0)结果是错误的,意味着已经发生了并发的有序性问题。为什么会出现(0,0)结果呢?可能在程序的执行过程中发生了指令重排序。对于线程t1来说,可能a=1和x=b这两个语句的赋值操作顺序被颠倒了,对于线程t2来说,可能b=1和y=a这两个语句的赋值操作顺序被颠倒了,从而出现了(x,y)值为(0,0)的错误结果。

 

  什么是指令重排序?

  一般来说,CPU为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行顺序同代码中的先后顺序一致,但是它会保证程序最终的执行结果和代码顺序执行的结果是一致的。

  重排序也是单核时代非常优秀的优化手段,有足够多的措施保证其在单核下的正确性。在多核时代,如果工作线程之间不共享数据或仅共享不可变数据,重排序也是性能优化的利器。然而,如果工作线程之间共享了可变数据,由于两种重排序的结果都不是固定的,因此会导致工作线程似乎表现出了随机行为。指令重排序不会影响单个线程的执行,但是会影响多个线程并发执行的正确性。

  事实上,输出了乱序的结果,并不代表一定发生了指令重排序,内存可见性问题也会导致这样的输出。但是,指令重排序也是导致乱序的原因之一。

  总之,要想并发程序正确地执行,必须要保证原子性、可见性以及有序性。只要有一个没有得到保证,就有可能会导致程序运行不正确。

  

 

  

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注盛行IT的更多内容!

 



推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 本文介绍了一个React Native新手在尝试将数据发布到服务器时遇到的问题,以及他的React Native代码和服务器端代码。他使用fetch方法将数据发送到服务器,但无法在服务器端读取/获取发布的数据。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
author-avatar
手机用户2502881415
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有