成都网站开发环球中心,郑州上街区网站建设公司,免费建站推广,视频号怎么推广流量转载自 Java的并发编程中的多线程问题到底是怎么回事儿#xff1f;
在我之前的一篇《再有人问你Java内存模型是什么#xff0c;就把这篇文章发给他。》文章中#xff0c;介绍了Java内存模型#xff0c;通过这篇文章#xff0c;大家应该都知道了Java内存模型的概念以及作…转载自 Java的并发编程中的多线程问题到底是怎么回事儿
在我之前的一篇《再有人问你Java内存模型是什么就把这篇文章发给他。》文章中介绍了Java内存模型通过这篇文章大家应该都知道了Java内存模型的概念以及作用这篇文章中谈到在Java并发编程中通常会遇到三个问题即原子性问题、一致性问题和有序性问题。
上面一篇文章简单介绍了一下由于各种原因会导致多线程场景下可能存在原子性、一致性和有序性问题。但是并没有深入这篇文章就来在之前的基础上再来看一下并发编程中这些问题都是哪来的
首先我们还是从操作系统开始先来了解一些基础知识。 CPU时间片
很多人都知道现在我们用到操作系统无论是Windows、Linux还是MacOS等其实都是多用户多任务分时操作系统。使用这些操作系统的“用户”是可以“同时”干多件事的这已经是日常习惯了并没觉得有什么特别。
但是实际上对于单CPU的计算机来说在CPU中同一时间是只能干一件事儿的。
为了看起来像是“同时干多件事”分时操作系统是把CPU的时间划分成长短基本相同的时间区间,即”时间片”通过操作系统的管理把这些时间片依次轮流地分配给各个“用户”使用。
如果某个“用户”在时间片结束之前整个任务还没有完成“用户”就必须进入到就绪状态放弃CPU等待下一轮循环。此时CPU又分配给另一个“用户”去使用。 CPU 就好像是一个电话亭他可以开放给所有用户使用但是他有规定每个用户进入电话亭之后只能使用规定时长的时间。如果时间到了用户还没打完电话那就会被要求去重新排队。 不同的操作系统在选择“用户”分配时间片的调度算法是不一样的常用的有FCFS、轮转、SPN、SRT、HRRN、反馈等由于不是本文重点就不展开了。 这个电话亭可以允许哪个用户进入打电话是有不同的策略的不同的电话亭规定不同有的电话亭采用排队机制FCFS、有的优先分配给打电话时间最短的人SPN等 进程与线程
前面介绍CPU时间片的时候提到了CPU会根据不同的调度算法把时间片分配给“用户”这里的“用户”在以前指的是进程随着操作系统的不断发展现在一般指线程。
在过去没有线程的操作系统中资源的分配和执行都是由进程完成的。随着技术的发展为了减少由于进程切换带来的开销提升并发能力操作系统中引入线程。把原本属于进程的工作一分为二进程还是负责资源的分配而线程负责执行。
也就是说进程是资源分配的基本单位而线程是调度的基本单位。 多线程中的并发问题
了解了以上的和硬件及操作系统有关的基础知识以后我们再来看下在多线程场景中有哪些并发问题。
关于并发编程中的原子性、可见性和有序性问题我在《内存模型》一文介绍过。
文中提到缓存一致性问题其实就是可见性问题。而处理器优化是可以导致原子性问题的。指令重排即会导致有序性问题。有部分读者对这部分不是很理解。由于上一篇文章主要介绍内存模型并没有展开分析只是给了个结论这里再针对这部分深入分析一下。
由于缓存一致性问题导致可见性问题在《内存模型》中介绍的很清晰了这里就不赘述了主要结合本文来分析下原子性问题和有序性问题。
原子性问题
我们说原子性问题其实指的是多线程场景中操作如果不能保证原子性会导致处理结果和预期不一致。
前面我们提到过线程是CPU调度的基本单位。CPU有时间片的概念会根据不同的调度算法进行线程调度。所以在多线程场景下就会发生原子性问题。因为线程在执行一个读改写操作时在执行完读改之后时间片耗完就会被要求放弃CPU并等待重新调度。这种情况下读改写就不是一个原子操作。 就好像我们去电话亭打电话一共有三个步骤查找电话拨号交流。由于我们在电话亭中可以停留的时间有限有可能刚刚找到电话号码时间到了就被赶出来了。 在单线程中一个读改写就算不是原子操作也没关系因为只要这个线程再次被调度这个操作总是可以执行完的。但是在多线程场景中可能就有问题了。因为多个线程可能会对同一个共享资源进行操作。
比如经典的 i 操作对于一个简单的i操作一共有三个步骤load , add,save 。共享变量就会被多个线程同时进行操作这样读改写操作就不是原子的操作完之后共享变量的值会和期望的不一致举个例子如果i1,我们进行两次i操作我们期望的结果是3但是有可能结果是2。 有序性问题
而且我们知道除了引入了时间片以外由于处理器优化和指令重排等CPU还可能对输入代码进行乱序执行比如load-add-save 有可能被优化成load-save-add 。这就是有序性问题。 我们打电话的时候除了可能被中途赶出来以外本来正常步骤是要查找电话、拨号、交流的。但是电话亭非要给我们优化成查找电话、交流、拨号。这肯定不是我们想要的啊。 还是刚刚的i操作在满足了原子性的情况下如果没有满足有序性那么得到的结果可能也不是我们想要的。 总结
本文主要介绍了并发编程中会导致原子性和有序性问题的原因关于可见性请参考《内存模型》。关于这三种问题的解决方案在《内存模型》也有介绍更多的可以参考多线程相关书籍。Hollis后续也会出更多文章再深入分析敬请期待。