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

打工人!肝了这套多线程吧!壹

开篇闲扯    一年又一年,年年多线程。不论你是什么程序员,都逃脱不了多线程并发的魔爪。因为它从盘古开天辟地的时候就有了,就是在计算机中对现实世界的一种抽象。因此,放轻松别害怕,肝

开篇闲扯

    一年又一年,年年多线程。不论你是什么程序员,都逃脱不了多线程并发的魔爪。因为它从盘古开天辟地的时候就有了,就是在计算机中对现实世界的一种抽象。因此,放轻松别害怕,肝了这系列的多线程文章,差不多能吊打面试官了(可别真动手...)。

并发症

    并发问题,曾经在单核单线程的机器上是不存在的(不是不想,是做不到)。假如把计算机看成一个木桶,那么跟我们Java开发人员关系最大的就是CPU、内存、IO设备。这三块木板发展至今,彼此之间也形成了较大的性能差异。CPU的核心数线程数在不断增多,内存的速度却跟不上CPU的步伐,同理IO设备也没能跟上内存的步伐。于是就加缓存,经过科学论证三级缓存最靠谱,于是就有了常见的CPU三级缓存。然后前辈们再对操作系统做各类调度层面的深度优化,通过软硬兼施的手法,使得软件与硬件的完美结合,才有如今繁荣的互联网。而我们不过是在这座城市里的打工人罢了。
言归正传,本文将分别说明在并发世界里的“三宗罪”:可见性原子性有序性


罪状一:可见性

前文中有说到CPU的发展经历了从单核单线程到现在的多核心多线程,而内存的读写性能却供应不上CPU的处理能力,于是就增加了缓存,至于前文中提到的三级缓存为什么是三级,不在本文讨论范围,有兴趣自己看去。。。

为什么会有可见性问题?

    在单核心时代,所有的线程都是交给一个CPU串行执行,因此不论有多少线程都是排队执行,也就不会形成线程A与B同时竞争target变量的竞争状态,如图一。
打工人!肝了这套多线程吧!壹
    来到多核心多线程时代,每颗CPU都有各自的缓存,如果多个线程分别在不同的CPU上运行,且需要同时操作同一个数据。而每颗CPU在处理内存中的数据时,会先将目标数据缓存到CPU缓存中。这时候CPU们各干各的,也不管目标值有没有被其他CPU修改过,自己在缓存中修改后不管三七二十一就写回去,这肯定是不行的啊兄弟...,而这就是我们Java中常说的数据可见性问题,再追根溯源就是:CPU级别的缓存一致性协议。后边文章会详细解释(别问具体时间,问了就是明天)。

可见性问题怎么解决?

    这个简单,如果仅仅是解决可见性,那就Volatile关键字用起来(也不是万能的,慎重考虑),它会将共享变量数据从线程工作内存刷新到主存中,而这个关键字的实现基础是Java规范的内存模型,注意,这里要和JVM内存模型区分开,两者是不一样的东西。那么Java内存模型又是什么样的,为啥设计这个内存模型,有哪些好处?下篇详细解释!本文就先放一张简单的图:
打工人!肝了这套多线程吧!壹


罪状二:原子性

    大家都知道CPU的运行时间是分片进行的,可能CPU这段时间在执行我写的if-else,下一时刻由于操作系统的调度当前线程丢失时间片,又执行其他线程任务去了(呸!渣男)。打断了我当前线程的一个或者多个操作流程,这就是原子性被破坏了,也就是多线程无锁情况下的ABA问题。跟我们期望的完全不一样啊,还是看图说话:
打工人!肝了这套多线程吧!壹
    解释一下就是:想要得到temp为2的结果,但是只能得到1,原因就是运行A线程的CPU干别的去了,而这时候B线程所在的CPU后发制A,抢先完成了++的操作并写回内存,但是A不知道,还傻傻的以为它的到的是temp的初恋,又傻傻的写会去,然后就心态崩了呀!偷袭~(出错)


罪状三:有序性

    如果说原子性问题是硬件工程师挖的坑(CPU别切换多好),那有序性就妥妥的是软件工程师下了老鼠夹子(夸张了啊,其实都是为了效率)。之所以存在有序性问题,完全是编译大神们对我们的关爱,知道我们普通Coder对性能的要求是能跑就行。

    因此,在Java代码在编译时期动了手脚,比如说:锁消除、锁粗化(需要进行逃逸性分析等技术手段)或者是将A、B两段操作互换顺序。但是,所有的这一切都不能影响源码在单线程执行情况下的最终结果,即as-if-serial语义。这是个很顶层的协议,不论是编译器、运行时状态还是处理器都必须遵守该语义。这是保证程序正确性的大前提。当然,编译器不仅仅要准守as-if-serial语义,还要准守以下八大规则--Happens-Before规则(八仙过海各显神通):

什么是Happens-Before规则?

    一段程序中,前面运行后的结果,对后面的操作来说均可见。即:不论怎么编译优化(编译优化的文章以后会写,关注我,全免费)都不能违背这一指导思想。不能忘本

规则名称 解释
程序顺序规则 在一个线程中,按照程序的顺序,前面的操作先发生于后续的操作
volatile变量规则 对volatile变量进行写操作时,要优先发生于对这个变量的读操作,可以理解为禁止指令重排但实际不完全是一个意思
线程start()规则 很好理解,线程的start()操作要优先发生于该线程中的所有操作(要先有鸡才能有蛋)
线程join()规则 线程A调用线程B的join()并成功返回结果时,线程B的任意操作都是先于join()操作的。
管理锁定规则 在java中以Synchronized为例来说就是加解锁操作要成对且解锁操作在加锁之后
对象终结规则 一个对象的初始化完成happen—before它的finalize()方法的开始
传递性 即A操作先于B发生,B先于C发==>A先于C发生

:文章里所有类似“先于”、“早于”等词并不严谨不能和Happens-Before划等号,只是这样说更好理解,较为准确的含义是:操作结果对后者可见。

其实,总结来说就是JMM、编译器和程序员之间的关系。

JMM对程序员说:你按顺序写,执行结果就是按照你写的顺序执行的,有BUG就是你自己的问题。
程序员:好的,听你的!
JMM对编译器说:你不能随便改变程序员的代码顺序,我都跟他承诺写啥是啥了,别搞错了。
编译器:收到!(可我还是想优化,我不影响你不就行了,这优化我做定了,奥利给!)

于是就有了这些规则,而对于我们CRUD Boy来说都是不可见的,了解一下就OK!

感谢各位看官!

更多文章请扫码关注或微信搜索Java栈点公众号!

打工人!肝了这套多线程吧!壹


推荐阅读
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • Java 11相对于Java 8,OptaPlanner性能提升有多大?
    本文通过基准测试比较了Java 11和Java 8对OptaPlanner的性能提升。测试结果表明,在相同的硬件环境下,Java 11相对于Java 8在垃圾回收方面表现更好,从而提升了OptaPlanner的性能。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • java drools5_Java Drools5.1 规则流基础【示例】(中)
    五、规则文件及规则流EduInfoRule.drl:packagemyrules;importsample.Employ;ruleBachelorruleflow-group ... [详细]
  • 第七课主要内容:多进程多线程FIFO,LIFO,优先队列线程局部变量进程与线程的选择线程池异步IO概念及twisted案例股票数据抓取 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 本文整理了Java中org.apache.hadoop.hive.ql.plan.ExprNodeColumnDesc.getTypeInfo()方法的一些代码示例,展 ... [详细]
  • 开发笔记:Python之路第一篇:初识Python
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Python之路第一篇:初识Python相关的知识,希望对你有一定的参考价值。Python简介& ... [详细]
author-avatar
只是遇不到他_740
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有