当前位置:首页 > Java入门 > 正文内容

java并发编程:并发问题的根源及主要解决方法包括(java并发编程详解)

abcsky887个月前 (10-29)Java入门100

本篇文章给大家谈谈

并发问题的根源在哪首先,我们要知道并发要解决的是什么问题?并发要解决的是单进程情况下硬件资源无法充分利用的问题而造成这一问题的主要原因是CPU-内存-磁盘三者之间速度差异实在太大如果将CPU的速度比作火箭的速度,那么内存的速度就像火车,而最惨的磁盘,基本上就相当于人双腿走路。

java并发编程:并发问题的根源及主要解决方法包括(java并发编程详解)

这样造成的一个问题,就是CPU快速执行完它的任务的时候,很长时间都会在等待磁盘或是内存的读写计算机的发展有一部分就是如何重复利用资源,解决硬件资源之间效率的不平衡,而后就有了多进程,多线程的发展并且演化出了各种为多进程(线程)服务的东西:。

CPU增加缓存机制,平衡与内存的速度差异增加了多个概念,CPU时间seo网站推广做什么片,程序计数器,线程切换等,用以更好得服务并发场景编译器的指令优化,希望在内部充分利用硬件资源但是这样一来,也会带来新的并发问题,归结起来主要有三个。

由于缓存导致的可见性问题线程切换带来的原子性问题编译器优化带来的有序性问题

我们分别介绍这几个:缓存导致的可见性CPU为了平衡与内存之间的性能差异,引入了CPU缓存,这样CPU执行指令修改数据的时候就可以批量直接读写CPU缓存的内存,一个阶段后再将数据写回到内存但由于现在多核CPU技术的发展,各个线程可能运行在不同CPU核上面,每个CPU核各有各自的CPU缓存。

前面说到对变量的修改通常都会先写入CPU缓存,再写回内存这就会出现这样一种情况,线程1修seo网站推广做什么改了变量A,但此时修改后的变量A只存储在CPU缓存中这时候线程B去内存中读取变量A,依旧只读取到旧的值,这就是可见性问题。

线程切换带来的原子性为了更充分得利用CPU,引入了CPU时间片时间片的概念进程或线程通过争用CPU时间片,让CPU可以更加充分地利用比如在进行读写磁盘等耗时高的任务时,就可以将宝贵的CPU资源让出来让其他线程去获取CPU并执行任务。

但这样的切换也会导致问题,那就是会破坏线程某些任务的原子性比如java中简单的一条语句count += 1映射到CPU指令有三条,读取count变量指令,变量加1指令,变量写回指令虽然在高级语言(java)看来它就是一条指令,但实际上却是三条CPseo网站推广做什么U指令,并且这三条指令的原子性无法保证。

也就是说,可能在执行到任意一条指令的时候被打断,CPU被其他线程抢占了而这个期间变量值可能会被修改,这里就会引发数据不一致的情况了所以高并发场景下,很多时候都会通过锁实现原子性而这个问题也是很多并发问题的源头。

编译器优化带来的有序性因为现在程序员编写的都是高级语言,编译器需要将用户的代码转成CPU可以执行的指令同时,由于计算机领域的不断发展,编译器也越来越智能,它会自动对程序员编写的代码进行优化,而优化中就有可能出现实际执行代码顺序和编写的代码顺序不一样的情况。

而这种破坏程序有序性的行为,在有些时候会出现一些非常微妙且难以察觉的并发编程bug。举个简单的seo网站推广做什么例子,我们常见的单例模式是这样的:

即通过两段判断加锁来保证单例的成功生成,但在极小的概率下,可能会出现异常情况原因就出现在sInstance = new Singleton();这一行代码上这行代码,我们理解的执行顺序应该是这样:为Singleton象分配一个内存空间。

在分配的内存空间实例化对象把Instance 引用地址指向内存空间但在实际编译的过程中,编译器有可能会帮我们进行优化,优化完它的顺序可能变成如下:为Singleton对象分配一个内存空间把instance 引用地址指向内存空间。

在分配的内存空间实例化对象 按照优化完的顺序,当并发访问的时候,可能会出现这样的情况A线程进入方法进行seo网站推广做什么第1次instance == null判断此时A线程发现instance 为null 所以对Singleton.class加锁。

然后A线程进入方法进行第2次instance == null判断然后A线程发现instance 为null,开始进行对象实例化为对象分配一个内存空间6.把Instance 引用地址指向内存空间(而就在这个指令完成后,线程B进入了方法)。

B线程首先进入方法进行第1次instance == null判断B线程此时发现instance 不为null ,所以它会直接返回instance (而此时返回的instance 是A线程还没有初始化完成的对象)。

最终线程B拿到的instseo网站推广做什么ance 是一个没有实例化对象的空内存地址,所以导致instance使用的过程中造成程序错误解决办法很简单,可以给sInstance对象加上一个关键字,volatile,这样编译器就不会乱优化,有关volatile的具体内容后续再细说。

主要解决办法通过上面的介绍,其实可以归纳无论是CPU缓存,线程切换还是编译器优化乱序,出现问题的核心都是因为多个线程要并发读写某个变量或并发执行某段代码那么我们可以控制,一次只让一个线程执行变量读写就可以了,这就是。

互斥而在某些时候,互斥还不够,还需要一定的条件比如一个生产者一个消费者并发,生产者向队列存东西,消费者向队列拿东西那么生产者写的时候要保证存的时候队seo网站推广做什么列不是满的,消费者要保证拿的时候队列非空这种线程与线程间需要通信协作的情况,称为。

同步,同步可以说是更复杂的互斥既然知道了并发编程的根源以及同步和互斥,那我们来看看有哪些解决的思路其实一共也就三种:避免共享Immutability(不变性)管程及其他工具下面我们分别说说这三种方案的优缺点。

避免共享 我们先来说说避免共享,其实避免共享说是线程本地存储技术,在java中指的一般就是ThreadlocalThreadLocal会为每个线程提供一个本地副本,每个线程都只会修改自己的ThreadLocal变量。

这样一来就不会出现共享变量,也就不会出现冲突了其实现原理是在ThreadLocal内部维护一个seo网站推广做什么ThreadLocalMap,每次有线程要获取对应变量的时候,先获取当前线程,然后根据不同线程取不同的值,典型的以空间换时间。

所以ThreadLocal还是比较适用于需要共享资源,且资源占用空间不大的情况比如一些连接的session啊等等但是这种模式应用场景也较为有限,比如需要同步情况就难以胜任Immutability(不变性) 。

Immutability在函数式中用得比较多,函数式编程的一个主要目的是要写出无副作用的代码,有关什么是无副作用可以参考我以前的文章Scala函数式编程指南(一) 函数式思想介绍而无副作用的一个主要特点就是变量都是Immutability即不可变的,即创建对象后不会seo网站推广做什么再修改对象,比如scala默认的变量和数据结构都是不可变的。

而在java中,不变性变量即通过final修饰的变量,如String,Long,Double等类型都是Immutability的,它们的内部实现都是基于final关键字的那这又和并发编程有什么关系呢?其实啊,并发问题很大部分原因就是因为线程切换破坏了原子性,这又导致线程随意对变量的读写破坏了数据的一致性。

而不变性就不必担心这个问题,因为变量都是不变,不可写只能读的在这种编程模式下,你要修改一个变量,那么只能新生成一个这样做的好处很明显,但坏处也是显而易见,那就是引入了额外的编程复杂度,丧失了代码的可读性和易用性。

因为如此,不变性的并发seo网站推广做什么解决方案其实相对而已没那么广泛,其中比较有代表性的算是Actor并发编程模型,我以前也有讨论过,有兴趣可以看看Actor模型浅析 一致性和隔离性,这种编程模型和常规并发解决方案有很显著的差异。

按我的了解,Acctor模式多用在分布式系统的一些协调功能,比如维持集群中多个机器的心跳通信等等如果在单机并发环境下,还是下面要介绍的管程类工具才是利器管程及其他工具 其实最早的操作系统中,解决并发问题用的是信号量,信号量通过两个原子操作wait(S),和signal(S)(俗称P,V操作)来实现访问资源互斥和同步。

比如下面这个小例子:

虽然信号量方便有效,但信号量要对每个共享资源都实现对应的P和V操作,这seo网站推广做什么使得并发编程中可能要出现大量的P,V操作,并且这部分内容难以抽象出来为了更好地实现同步互斥,于是就产生了管程(即Monitor,也有翻译为监视器),值得一提的是,管程也有几种模型,分别是:Hasen模型,Hoare模型和MESA模型。

其中MESA模型应用最广泛,java也是参考自MESA模型这里简单介绍下管程的理论知识,这部分内容参考自进程同步机制-----为进程并发执行保驾护航,希望了解更多管程理论知识的童鞋可以看看我们来通过一个经典的生产-消费队列来解释,如下图

我们先解释下图中右半部分的内容,右上角有一个等待调用的线程队列,管程中每次只能有一个线程在执行任务,所以多个任务需要等待然后是各个seo网站推广做什么名词的意思,生产-消费需要往队列写入和取出东西,这里的队列就是共享变量,

对共享资源进行操作称之为过程(入队和出队两个过程)而向队列写入和取出是有条件的,写入的时候队列必须是非满的,取出的时候队列必须是非空的,这两个条件被称为条件变量然后再来看看左半部分的内容,假设线程T1读取共享变量(即队列),此时发现队列为空(条件变量之一),那么T1此时需要等待,去哪里等呢?去条件变量。

队列不能为空对应的队列中去等待此时另一个线程T2向共享变量队列写数据,通过了条件变量队列不能满,那么写完后就会通知线程T1但因为管程的限制,管程中只能有一个线程在执行,所以T1线程不能立即执行,它会回到右上角的线程等待队列等seo网站推广做什么待(不同的管程模型在这里是有分歧的,比如Hasen模型是立即中断T2线程让队列中下一个线程执行)。

解释完这个图,管程的概念也就呼之欲出了,hansen对管程的定义如下:一个管程定义了一个数据结构和能力为并发进程所执行(在该数据结构上)的一组操作,这组操作能同步进程和改变管程中的数据本质上,管程是对共享资源以及对共享资源的操作抽象成变量和方法,要操作共享变量仅能通过管程提供的方法(比如上面的入队和出队)间接访问。

所以你会发现管程其实和面向对象的理念是十分相近的,在java中,主要提供了低层次了synchronized关键字和wait(),notify()等方法同时还提供了高层次的ReenTranseo网站推广做什么tLock和Condition来实现管程模型。

举报/反馈

扫描二维码推送至手机访问。

版权声明:本文由海南拓宏网络科技工作室发布,如需转载请注明出处。

本文链接:http://4blc.com/post/752.html

分享给朋友:

“java并发编程:并发问题的根源及主要解决方法包括(java并发编程详解)” 的相关文章

java是什么类型的编程语言(java是什么意思怎么读)

本篇文章给大家谈谈 我刚开始学习Java的时候,很长一段时间都有这么个疑惑?Java到底是啥?它有什么用?自己也看过不少的课程和书,大部分都是从Java的发展史开始讲,总之就是那些什么Java历史悠久,Java很优秀,Java越来越牛,用的人越来越多,什么编程语言排行榜常年第一,大...

达内java培训费多少(达内学java培训课程)

本篇文章给大家谈谈 随着高校毕业生就业压力增大,在激烈的人才竞争中,如果没有一技之长很难在社会中立足目前全球已经快速步入移动互联时代,软件开发成为最紧缺的岗位,根据相关部门数字统计,在所有软件开发类人才的需求中,对Java工程师的需求达到全部需求量的60%~70%.并且Java工程...

软件技术java方向就业前景(软件技术java方向就业岗位)

本篇文章给大家谈谈 作为一名计算机专业的研究生教育工作者,我来回答一下这个问题首先,对于计算机科学与技术专业的同学来说,考研时可以继续选择本专业,相对于其他计算机相关专业来说,计科专业的人才培养规模还是比较大的,而且计科专业的研究生教育阶段经过多年的建设,不仅学科体系成熟度比较高,...

计算机科学与技术什么专业好找工作(计算机科学与技术什么专业好就业)

今天给各位分享 原标题:最火热的计算机科学与技术专业,适合你吗?计算机相关专业作为近年来最火热的专业,深受许多学子热捧,成为他们报志愿时的首选计算机相关专业究竟是什么?哪些高校实力强劲呢?接下来,让我们深入了解一下计算机相关专业。 什么是计算机科学与技术?计算机科学与技术是...

面试3小时说尽快给我答复(面试等了3个小时)

本文分享给大家的是: 文章格式有些变化,具体原因中秋发过文章解释,主要是想在格式调整上少花些心思,把精力用在内容本身上,毕竟刚入职新工作嘛~‍先直接说一下结论从 6 月初开始,到 8 月末结束,这个期间一直在面试,大约从面试场数来看,。 70 多场,每个岗位走完全部流程大概...

kotlin ::class.java(kotlin.jvm.internal.intrinsics)

本篇文章给大家谈谈 【CSDN 编者按】Kotlin 和 Java 是如何解决 Null 问题?本文作者分享了解决思路原文链接:https://blog.frankel.ch/null-safety-java-vs-kotlin/。 未经授权,禁止转载!作者 | Nicol...