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

『图解Java并发编程系列』10张图告诉你Java并发多线程那些破事 头发很多的程序员:『师父,这个批量处理接口太慢了,有什么办法可以优化?』 架构师:『试试使用多线程优化』 第二天 头发很多的程序员:『师父,我已经使用了多线程为什么接口还变慢了?』 架构师:『去给我买杯咖啡,我写篇文章告诉你』 ……吭哧吭哧买咖啡去了 在实际工作中,错误使用多线程非但不能提高效率还可能使程序崩溃。以在路上开车为例: 在一个单向行驶的道路上,每辆汽车都遵守交通规则,这时候整体通行是正常的。『单向车道』意味着『一个线程』,『多辆车』意味着『多个job任务』。 单线程顺利. 阅读详情

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中的死锁与资源竞争问题 死锁是指两个或多个线程在执行过程中,因争夺资源而造成的一种互相等待的现象。简单来说,线程 A 持有锁 1,等待锁 2;线程 B 持有锁 2,等待锁 1,导致这两个线程互相等待,无法继续执行。在Java中,多线程并发编程提供了高效的并行计算能力,但同时也带来了死锁、资源竞争、线程安全等问题。通过合理使用Java提供的同步机制,如和等,可以有效地避免这些问题。最关键的是理解并发编程的基本原理,掌握常见的线程安全问题和死锁避免策略,从而编写出更稳定、高效的并发程序。 阅读详情

相关推荐

Java 多线程并发编程全面指南

Java 作为一门成熟的企业级编程语言,提供了丰富的多线程并发编程支持,使开发者能够构建高性能、高并发的应用程序。然而,多线程编程也带来了复杂性,包括线程安全、死锁、竞态条件等问题。本文将全面介绍 Java 多线程并发编程的各个方面。

vvilkin的学习备忘 1378

Java并发编程中的多线程问题到底是怎么回事儿?你了解多少

前言 多线程并发编程Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们来说极其重要,下面跟我一起开启本的学习之旅吧。 正文 线程与进程 1 线程:进程中负责程序执行执行单元线程本身依靠程序进行运行线程是程序中的顺序控制流,只能使用分配给程序的资源和环境 2 进程:执行中的程序一个进程至少包含一个线程 3 单线程:程序中只存在一个线程,实际上主方法就是一个主线程 4 多线程:在一个程序中运行多个任务目的是更好地使用CPU资源 线程的实现 继承Thread类

如果频繁就私信一下呢 336

汪文君-并发编程三个阶段的实战视频.txt

汪文君录制的高并发编程视频,含有1,2,3个阶段的,从多线程编程基础到并发编程JUC都有涵盖。

JAVA如何解决并发问题

并发问题的根源在哪 首先,我们要知道并发要解决的是什么问题并发要解决的是单进程情况下硬件资源无法充分利用的问题。而造成这一问题的主要原因是CPU-内存-磁盘三者之间速度差异实在太大。如果将CPU的速度比作火箭的速度,那么内存的速度就像火车,而最惨的磁盘,基本上就相当于人双腿走路。 这样造成的一个问题,就是CPU快速执行完它的任务的时候,很长时间都会在等待磁盘或是内存的读写。 计算机的发展有一部分就是如何重复利用资源,解决硬件资源之间效率的不平衡,而后就有了多进程,多线程的发展。并且演化出了各种为多进

wzl1217333452的博客 3052

java多线程

多线程: 1、什么是进程?什么是线程? 进程是一个应用程序。(一个进程是一个软件) 线程是一个进程中的执行场景/执行单元。 一个进程可以启动多个线程。 2、对于java程序来说,当DOS命令窗口输入: java HelloWorld回车之后。 会先启动JVM,而JVM就是一个进程。 JVM再启动一个主线程调用main方法。 同时再启动一个垃圾回收线程负责看护,回收垃圾。 最起码,现在的java程序中至少有两个线程并发一个是垃圾回收...

qq_45796208的博客 1414

Java多线程编程

为什么会排队等待? 下面的这个简单的 Java 程序完成四项不相关的任务。这样的程序有单个控制线程,控制在这四个任务之间线性地移动。此外,因为所需的资源 ? 打印机、磁盘、数据库和显示屏 — 由于硬件和软件的限制都有内在的潜伏时间,所以每项任务都包含明显的等待时间。因此,程序在访问数据库之前必须等待打印机完成打印文件的任务,等等。如果您正在等待程序的完成,则这是对计算资源和您的时间的一种拙劣使用...

iteye_4808的博客 380

Java并发编程多线程会导致的问题

1. 线程安全问题 1.1 什么是线程安全? 某权威作者定义:“当多个线程访问同一个对象时,如果不用考虑这些线程的调度,也不需要额外的同步,调用这个对象的行为都可以获得正确的结果,那么这个对象时线程安全的“。 通俗易懂的定义:当多个线程访问同一个对象时,不需要做额外的任何处理,像单线程编程一样,程序可以正常运行,就可以称为线程安全。 1.2 线程安全问题有哪些? 运行结果错误:a++多线程下...

qq_36986015的博客 1252

Java并发性和多线程

转载自http://ifeve.com/java-concurrency-thread-directory/ 简要总结 并发性和多线程的区别和联系 多线程并发的一种实现。并发还可以使用分布式/多进程实现。 并发模型:并行工作者、流水线工作者/事件驱动模型 Java多线程两/四种创建方式 继承Thread,(匿名/不匿名)子类 实现Runnable接口,(匿名/不匿名)类 竞态条件...

Ole Wang的博客 355

如果把线程当作一个人来对待,所有问题都瞬间明白了

来源:公众号【编程新说】多线程问题都曾经困扰过每个开发人员,今天将从全新视角来解说,希望读者都能明白。强烈建议去运行下文章中的示例代码,自己体会下。问题究竟出在哪里?一...

Java团长的博客 864

Java开发多线程是如何解决安全问题的?

序言:提到线程安全,可能大家首先想到的是确保接口对共享变量的操作要具备 原子性。实际上,在多线程编程中我们需要同时关注可见性,顺序性和原子性。本篇文章将从这三个问题出发,结合实例详解volatile如何保u证可见性及一定程序上保证顺序性,同时例讲synchronized如何同时保证可见性和原子性,最后对比volatile和synchronized的适用场景。 1、多线程编程的三个核心概念 原子...

a6984021的博客 569

java并发编程--09(多线程编程的最佳实践)

1.使用本地变量(局部变量) 应该尽量的使用本地变量,而不是创建一个类或者实例的形式 2.使用不可变类 比如String类,它可以降低代码中同步数量 3.最小化锁的作用域范围 安达尔定理 简单来说就是被锁起来的代码越少越好 4.使用线程池Executor,而不是直接使用new Thread() 5.宁可使用同步,也不要使用线程的wait和notify...

最后的武艺集大成者 275

Java并发编程并发容器

Java并发编程并发容器 java.util.concurrent包提供了Java多线程情况下保证线程安全的一系列容器,包括Map,List,Set,Queue接口下的并发类实现。 1.ConcurrentHashMap ConcurrentMap接口下有两个实现,一个是ConcurrentHashMap和ConcurrentSkipListMap(支持并发排序)。 Concurren

刘越洋子 403
上一篇: 什么是MySQL数据库?看这一篇干货文章就够了!
下一篇: 这 9 个 Java 开源项目赶紧学起来!!!!
Javaaaaaaaaaaa13
博客等级 码龄2年 379粉丝 53原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值