转载自   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 。共享变量就会被多个线程同时进行操作,这样读改写操作就不是原子的,操作完之后共享变量的值会和期望的不一致,举个例子:如果i=1,我们进行两次i++操作,我们期望的结果是3,但是有可能结果是2。

有序性问题

而且,我们知道,除了引入了时间片以外,由于处理器优化和指令重排等,CPU还可能对输入代码进行乱序执行,比如load->add->save 有可能被优化成load->save->add 。这就是有序性问题。

我们打电话的时候,除了可能被中途赶出来以外,本来正常步骤是要查找电话、拨号、交流的。但是电话亭非要给我们优化成查找电话、交流、拨号。这肯定不是我们想要的啊。

还是刚刚的i++操作,在满足了原子性的情况下,如果没有满足有序性,那么得到的结果可能也不是我们想要的。

总结

本文主要介绍了并发编程中会导致原子性和有序性问题的原因,关于可见性请参考《内存模型》。关于这三种问题的解决方案在《内存模型》也有介绍,更多的可以参考多线程相关书籍。Hollis后续也会出更多文章再深入分析,敬请期待。

Java的并发编程中的多线程问题到底是怎么回事儿?相关推荐

  1. java高并发多线程架构_《Java高并发编程详解-多线程架构与设计》线程安全与数据同步...

    定义 共享资源:多个线程对同一资源访问(读写) 线程安全:多个线程对同一资源访问的数据是一致的. Synchronized使用 同步方法 同步代码块 深入 synchronized关键字 p66-67 ...

  2. python并发编程之semaphore(信号量)_Python 并发编程系列之多线程

    Python 并发编程系列之多线程 2 创建线程 2.1 函数的方式创建线程 2.2 类的方式创建线程 3 Thread 类的常用属性和方法 3.1 守护线程: Deamon 3.2 join()方法 ...

  3. Python并发编程系列之多线程

    1 引言 上一篇博文详细总结了Python进程的用法,这一篇博文来所以说Python中线程的用法.实际上,程序的运行都是以线程为基本单位的,每一个进程中都至少有一个线程(主线程),线程又可以创建子线程 ...

  4. java内存栅栏_内存屏障(Memory Barriers/Fences) - 并发编程中最基础的一项技术

    我们经常都听到并发编程,但很多人都被其高大上的感觉迷惑而停留在知道听说这一层面,下面我们就来讨论并发编程中最基础的一项技术:内存屏障或内存栅栏,也就是让一个CPU处理单元中的内存状态对其它处理单元可见 ...

  5. Java并发编程中的若干核心技术,向高手进阶

    来源:http://www.jianshu.com/p/5f499f8212e7 引言 本文试图从一个更高的视角来总结Java语言中的并发编程内容,希望阅读完本文之后,可以收获一些内容,至少应该知道在 ...

  6. synchronized 异常_由浅入深,Java 并发编程中的 Synchronized

    synchronized 作用 synchronized 关键字是 Java 并发编程中线程同步的常用手段之一. 1.1 作用: 确保线程互斥的访问同步代,锁自动释放,多个线程操作同个代码块或函数必须 ...

  7. java 线程由浅入深_由浅入深,Java 并发编程中的 Synchronized(一)

    synchronized 作用 synchronized 关键字是 Java 并发编程中线程同步的常用手段之一. 1.1 作用: 确保线程互斥的访问同步代,锁自动释放,多个线程操作同个代码块或函数必须 ...

  8. 由浅入深,逐步了解 Java 并发编程中的 Synchronized!

    作者 | sowhat1412  责编 | 张文 头图 | CSDN 下载自视觉中国 来源 | sowhat1412(ID:sowhat9094) synchronized 作用 synchroniz ...

  9. Java 并发编程中的死锁 ( Kotlin 语言讲解)

    什么是死锁? 在操作系统中的并发处理场景中, 进程对资源的持有与请求过程中,会产生死锁. Say, Process A has resource R1 , Process B has resource ...

最新文章

  1. 常见的神经网络求导总结!
  2. 中国半挂车行业投资前景预测与十四五投资战略规划分析报告2021年版
  3. 代码规范:在Keil5中使用代码格式化工具Astyle(插件)
  4. 计算机硬盘正在工作应特别注意避免,初级计算机考试题库
  5. Ooooops! 这通骚扰电话是AI机器人打的(浙大出品,中文很6)
  6. 从零基础入门Tensorflow2.0 ----一、3.1 实战深度神经网络
  7. 三年级计算机测试题,小学三年级信息技术考试试题
  8. iOS数据恢复工具PhoneRescue for Mac中文破解版
  9. 03.汇编语言和编译器
  10. 浅谈 MyBatis 缓存
  11. 计算机软件需求说明编制指南gb/t 9385-2008,GBT 9385-2008 计算机软件需求说明编制指南.pdf...
  12. python扩展包怎么安装_Python中扩展包的安装方法详解
  13. 论文阅读——Towards Adversarially Robust Object Detection
  14. 详细不啰嗦,电脑重装系统win10教程分享
  15. 在数据集Euroc v dataset下跑双目ORB-SLAM2
  16. intel编译器免费下载
  17. Yau 近代几何讲座
  18. JavaSE02(类与对象)
  19. Origin制图之热力图(hot-map)
  20. 程序语言编年史_Java的编年史和低延迟

热门文章

  1. 算法题目——第K大的数
  2. python编程中的小问题汇总
  3. [SpringSecurity]web权限方案_用户认证_自定义用户登录页面
  4. [Java基础]标准输入输出流
  5. hdu2648 Shopping-map容器
  6. 《C++ Primer》1.52节练习
  7. 逆向so_记一次APP的so层算法逆向(七)
  8. python分布式存储文件_python如何分布式存储文件的方法
  9. 树的度,结点,叶子结点,二叉树
  10. 统计学习笔记(4) 线性回归(1)