『图解Java并发编程系列』10张图告诉你Java并发多线程那些破事

article/2025/11/7 13:39:52

目录

线程安全问题

活跃性问题

性能问题

有态度的总结

 

头发很多的程序员:『师父,这个批量处理接口太慢了,有什么办法可以优化?』架构师:『试试使用多线程优化』第二天头发很多的程序员:『师父,我已经使用了多线程,为什么接口还变慢了?』架构师:『去给我买杯咖啡,我写篇文章告诉你』……吭哧吭哧买咖啡去了

在实际工作中,错误使用多线程非但不能提高效率还可能使程序崩溃。以在路上开车为例:

在一个单向行驶的道路上,每辆汽车都遵守交通规则,这时候整体通行是正常的。『单向车道』意味着『一个线程』,『多辆车』意味着『多个job任务』。

单线程顺利同行

单线程顺利同行

如果需要提升车辆的同行效率,一般的做法就是扩展车道,对应程序来说就是『加线程池』,增加线程数。这样在同一时间内,通行的车辆数远远大于单车道。

多线程顺利同行

多线程顺利同行

然而成年人的世界没有那么完美,车道一旦多起来『加塞』的场景就会越来越多,出现碰撞后也会影响整条马路的通行效率。这么一对比下来『多车道』确实可能比『单车道』要慢。

多线程故障

多线程故障

防止汽车频繁变道加塞可以采取在车道间增加『护栏』,那在程序的世界该怎么做呢?

程序世界中多线程遇到的问题归纳起来就是三类:『线程安全问题』『活跃性问题』『性能问题』,接下来会讲解这些问题,以及问题对应的解决手段。

线程安全问题

有时候我们会发现,明明在单线程环境中正常运行的代码,在多线程环境中可能会出现意料之外的结果,其实这就是大家常说的『线程不安全』。那到底什么是线程不安全呢?往下看。

原子性

举一个银行转账的例子,比如从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元,往账户B加上1000元,两个操作都成功才意味着一次转账最终成功。

试想一下,如果这两个操作不具备原子性,从A的账户扣减了1000元之后,操作突然终止了,账户B没有增加1000元,那问题就大了。

银行转账这个例子有两个步骤,出现了意外后导致转账失败,说明没有原子性。

原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

原子操作:即不会被线程调度机制打断的操作,没有上下文切换。

在并发编程中很多操作都不是原子操作,出个小题目:

i = 0; // 操作1
i++;   // 操作2
i = j; // 操作3
i = i + 1; // 操作4

上面这四个操作中有哪些是原子操作,哪些不是的?不熟悉的人可能认为这些都是原子操作,其实只有操作1是原子操作。

  • 操作1:对基本数据类型变量的赋值是原子操作;

  • 操作2:包含三个操作,读取i的值,将i加1,将值赋给i;

  • 操作3:读取j的值,将j的值赋给i;

  • 操作4:包含三个操作,读取i的值,将i加1,将值赋给i;

在单线程环境下上述四个操作都不会出现问题,但是在多线程环境下,如果不通过加锁操作,往往可能得到意料之外的值。

在Java语言中通过可以使用synchronize或者lock来保证原子性。

可见性

talk is cheap,先show一段代码:

/**
* Author: leixiaoshuai
*/
class Test {int i = 50;int j = 0;public void update() {// 线程1执行i = 100;}public int get() {// 线程2执行j = i;return j;}
}

线程1执行update方法将 i 赋值为100,一般情况下线程1会在自己的工作内存中完成赋值操作,却没有及时将新值刷新到主内存中。

这个时候线程2执行get方法,首先会从主内存中读取i的值,然后加载到自己的工作内存中,这个时候读取到i的值是50,再将50赋值给j,最后返回j的值就是50了。原本期望返回100,结果返回50,这就是可见性问题,线程1对变量i进行了修改,线程2没有立即看到i的新值。

可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

如上图每个线程都有属于自己的工作内存,工作内存和主内存间需要通过store和load等进行交互。

为了解决多线程可见性问题,Java语言提供了volatile这个关键字。当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。而普通共享变量不能保证可见性,因为变量被修改后什么时候刷回到主存是不确定的,另外一个线程读的可能就是旧值。

当然Java的锁机制如synchronize和lock也是可以保证可见性的,加锁可以保证在同一时刻只有一个线程在执行同步代码块,释放锁之前会将变量刷回至主存,这样也就保证了可见性。

关于线程不安全的表现还有『有序性』,这个问题会在后面的文章中深入讲解。

活跃性问题

上面讲到为了解决可见性问题,我们可以采取加锁方式解决,但是如果加锁使用不当也容易引入其他问题,比如『死锁』。

在说『死锁』前我们先引入另外一个概念:活跃性问题

活跃性是指某件正确的事情最终会发生,当某个操作无法继续下去的时候,就会发生活跃性问题。

概念是不是有点拗口,如果看不懂也没关系,你可以记住活跃性问题一般有这样几类:死锁活锁饥饿问题

(1)死锁

死锁是指多个线程因为环形的等待锁的关系而永远的阻塞下去。一图胜千语,不多解释。

(2)活锁

死锁是两个线程都在等待对方释放锁导致阻塞。而活锁的意思是线程没有阻塞,还活着呢。

当多个线程都在运行并且修改各自的状态,而其他线程彼此依赖这个状态,导致任何一个线程都无法继续执行,只能重复着自身的动作和修改自身的状态,这种场景就是发生了活锁。

![](/Users/ray/Library/Application Support/typora-user-images/image-20210408232019843.png)

如果大家还有疑惑,那我再举一个生活中的例子,大家平时在走路的时候,迎面走来一个人,两个人互相让路,但是又同时走到了一个方向,如果一直这样重复着避让,这俩人就是发生了活锁,学到了吧,嘿嘿。

(3)饥饿

如果一个线程无其他异常却迟迟不能继续运行,那基本是处于饥饿状态了。

常见有几种场景:

  • 高优先级的线程一直在运行消耗CPU,所有的低优先级线程一直处于等待;

  • 一些线程被永久堵塞在一个等待进入同步块的状态,而其他线程总是能在它之前持续地对该同步块进行访问;

有一个非常经典的饥饿问题就是哲学家用餐问题,如下图所示,有五个哲学家在用餐,每个人必须要同时拿两把叉子才可以开始就餐,如果哲学家1和哲学家3同时开始就餐,那哲学家2、4、5就得饿肚子等待了。

性能问题

前面讲到了线程安全和死锁、活锁这些问题会影响多线程执行过程,如果这些都没有发生,多线程并发一定比单线程串行执行快吗,答案是不一定,因为多线程有创建线程线程上下文切换的开销。

创建线程是直接向系统申请资源的,对操作系统来说创建一个线程的代价是十分昂贵的,需要给它分配内存、列入调度等。

线程创建完之后,还会遇到线程上下文切换

CPU是很宝贵的资源速度也非常快,为了保证雨露均沾,通常为给不同的线程分配时间片,当CPU从执行一个线程切换到执行另一个线程时,CPU 需要保存当前线程的本地数据,程序指针等状态,并加载下一个要执行的线程的本地数据,程序指针等,这个开关被称为『上下文切换』。

一般减少上下文切换的方法有:无锁并发编程CAS 算法使用协程等。

有态度的总结

多线程用好了可以让程序的效率成倍提升,用不好可能比单线程还要慢。

用一张图总结一下上面讲的:

image-20210412234350204

 

文章讲了多线程并发会遇到的问题,你可能也发现了,文章中并没有给出具体的解决方案,因为这些问题在Java语言设计过程中大神都已经为你考虑过了。

Java并发编程学起来有一定难度,但这也是从初级程序员迈向中高级程序员的必经道路,接下来的文章会带领大家逐个击破!

 

不如顺手点个赞?!

作者:雷架

Github 开源项目:https://github.com/smileArchitect/JavaMap

作者简介:
☕读过几年书:华中科技大学硕士毕业;
😂浪过几个大厂:华为、网易、百度……
😘一直坚信技术能改变世界,愿保持初心,加油技术人!

微信搜索公众号【爱笑的架构师】,关注这个对技术有追求且有趣的打工人。


http://chatgpt.dhexx.cn/article/E8BkrJFG.shtml

相关文章

Java基础-并发篇

3.1. JAVA 并发知识库 3.2. JAVA 线程实现/创建方式 3.2.1. 继承 Thread 类 ​ Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。启动线程的唯一方 法就是通过 Thread 类的 start()实例方法。start()方法是一个 native 方法,它将…

java并发总结

一、并发基础 ㅤ 1、进程与线程 ㅤ 进程 程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至 CPU,数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 I…

pytorch 图片分类,python 图片分类,resnet18 图片分类

pytorch 图片分类,python 图片分类,resnet18 图片分类,深度学习 图片分类 pytorch版本:1.5.0cu101 全部源码,可以直接运行。 下载地址:https://download.csdn.net/download/TangLingBo/12598435 网络是…

深度学习图片分类实战学习

开始记录学习深度学习的点点滴滴 深度学习图片分类实战学习 前言一、深度学习二、使用步骤1. 自建网络模型2. 进行深度学习的学习迁移 注意事项 前言 随着人工智能的不断发展,这门技术也越来越重要,很多人都开启了学习人工智能,本人开始记录…

关于图片的多标签分类(1)

最近还在处理人脸附件(眼镜,刘海,口罩,帽子)的multi-label分类。给自己普及一下常识性问题: 1)什么是multi-label分类? multi-label分类,常见一张图片中可以存在多个目…

svm实现图片分类(python)

目录 前言 knn vs. svm svm & linear classifier bias trick loss function regularization optimization 代码主体 导入数据及预处理 svm计算loss_function和梯度 验证梯度公式是否正确 比较运行时间 svm训练及预测,结果可视化 通过corss-validat…

图片分类-python

目的:做一个简易的图片分类。 使用到的算法:hog、surfsvm 图片集:cifar-10、cifar-100、stl-10、自制图片集 分类完整代码链接 使用说明: 1.cifar-10、cifar-100和stl-10直接解压 2.自制图片集文件夹结构: ├…

CNN图片分类

最近在阅读一些AI项目,写入markdown,持续更新,算是之后也能回想起做法 项目 https://github.com/calssion/Fun_AI image classify(图片分类) CNN classify dogs and cats(猫狗二分类) Tutorial(教程):https://developers.google.com/mach…

深度学习之图像分类

第一篇CSDN文章,写的不好,还请各位大佬指正。万事开头难,千里之行始于足下! 1.什么是图像分类 图像分类,核心是从给定的分类集合中给图像分配一个标签的任务。实际上,这意味着我们的任务是分析一个输入图…

关于图像分类

https://www.zhihu.com/question/57075015/answer/194397802https://www.zhihu.com/question/57075015/answer/194397802 先定义一下图像分类,一般而言,图像分类分为通用类别分类以及细粒度图像分类 那什么是通用类别以及细粒度类别呢?这里…

(一)图像分类任务介绍 Image Classification

目录 一、什么是图像分类任务?它有哪些应用场景? 二、图像分类任务的难点? 三、基于规则的方法是否可行? 四、什么是数据驱动的图像分类范式? 数据集构建 分类器设计与学习 分类器决策 五、常用的分类任务评价指…

图像分类的数据集

图像分类的数据集 1. MNIST2. Fashion-MNIST3.CIFAR-10和CIFAR-1004. Caltech 1015. ImageNet5.1 ImageNet是什么?5.2 ILSVRC 6. 各个数据集上的最新进展其他参考资料 1. MNIST MNIST数据集的一个样例 一般机器学习框架都使用MNIST作为入门,就像"He…

机器学习——图像分类

1 图像分类的概念 1.1 什么是图像分类? 图像分类,根据图像信息中所反映出来的不同特征,把不同类别的目标区分开来的图像处理方法 1.2 图像分类的难度 ●任何拍摄情 况的改变都将提升分类的难度 1.3 CNN如何进行图像分类 ●数据驱动型方法通…

图像分类算法

图像分类 参考链接1.前言2.K近邻与KMeans算法比较KNN原理和实现过程(1) 计算已知类别数据集中的点与当前点之间的距离:(2) 按照距离递增次序排序(3) 选取与当前点距离最小的k个点(4) 确定前k个点所在类别的出现频率(5) 返回前k个点出现频率最高的类别作为当前点的预…

图像分类方法总结

1. 图像分类问题描述 图像分类问题是计算机视觉领域的基础问题,它的目的是根据图像的语义信息将不同类别图像区分开来,实现最小的分类误差。具体任务要求是从给定的分类集合中给图像分配一个标签的任务。总体来说,对于单标签的图像分类问题&…

9.图片分类数据集

1. 图像分类数据集 MNIST数据集 [LeCun et al., 1998] 是图像分类中广泛使用的数据集之一,但作为基准数据集过于简单。 我们将使用类似但更复杂的Fashion-MNIST数据集。 %matplotlib inline import torch import torchvision from torch.utils import data from t…

CNN实现花卉图片分类识别

CNN实现花卉图片分 前言 本文为一个利用卷积神经网络实现花卉分类的项目,因此不会过多介绍卷积神经网络的基本知识。此项目建立在了解卷积神经网络进行图像分类的原理上进行的。 项目简介 本项目为一个图像识别项目,基于tensorflow,利用C…

常用图像分类网络

想对图像分类网络写个简要的概括,如有介绍不当之处,还望指出。 一、VGG网络 更新于2018年10月20日 参考博客:深度学习经典卷积神经网络之VGGNet 论文地址:VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITIO…

干货——图像分类(上)

这是译自斯坦福CS231n课程笔记image classification notes,由课程教师Andrej Karpathy授权进行翻译。本篇教程由杜客翻译完成。非常感谢那些无偿奉献的大师,在此代表所有爱好学习者向您们致敬,谢谢! 这是斯坦福大学的课程&#xf…

图像分类

图像物体分类与检测算法综述 转自《计算机学报》 目录 图像物体分类与检测算法综述 目录图像物体分类与检测概述物体分类与检测的难点与挑战物体分类与检测数据库物体分类与检测发展历程 图像物体分类与检测是计算机视觉研究中的两个重要的基本问题,也是图像分割、…