写点什么

史上最全的 Java 并发系列之并发编程的挑战

作者:自然
  • 2022 年 8 月 15 日
    广东
  • 本文字数:2246 字

    阅读完需:约 7 分钟

前言

文本已收录至我的 GitHub 仓库,欢迎 Star:https://github.com/bin392328206/six-finger

种一棵树最好的时间是十年前,其次是现在

我知道很多人不玩 qq 了,但是怀旧一下,欢迎加入六脉神剑 Java 菜鸟学习群,群聊号码:549684836 鼓励大家在技术的路上写博客

絮叨

从今天开始,我们进入一个新的领域学习,这个领域的学习,我打算跟着 Java 并发编程的艺术走,整本书是十一章,意味着我会写十篇左右博客在 Java 并发系列中,我希望通过我对这本书的总结,能让大家在面试和实际工作中运用上 Java 的并发知识

Java 并发编程的艺术

这本书还算可以吧,毕竟是阿里专家写的,然后我其实很早就买了,但是没怎么看,刚好因为大家喜欢我的文章,那我就很有动力去学习,然后把学到的知识分享给大家,



总共的章节有


  • 并发编程的挑战

  • Java 并发机制的底层实现

  • Java 内存模型

  • Java 并发编程基础

  • Java 中的锁

  • Java 并发容器和框架(前面讲 Java 容器提到过)

  • Java 中的 13 个原子类

  • Java 中并发的工具类

  • Java 中的线程池

  • Executor 框架

  • 实战(这个是案例)

JUC

这个肯定要知道是啥的,它是 Java JDK 中的一个安全包,别等面试官问你有没有接触过 JUC,你连听都没有听到过,就尴尬了。


在 Java 5.0 提供了 java.util.concurrent(简称 JUC)包,在此包中增加了在并发编程中很常用的工具类,用于定义类似于线程的自定义子系统,包括线程池,异步 IO 和轻量级任务框架;还提供了设计用于多线程上下文中的 Collection 实现等;



其实我们要讲的并发系列,也就是讲上图的内容来的。

并发编程的目的与挑战

并发编程的目的是为了让程序运行得更快。启动更多的线程并不一定就能让程序最大限度地并发执行。


希望通过多线程执行任务让程序运行得更快,会面临非常多的挑战。比如


  • 上下文切换 的问题

  • 死锁 的问题

  • 硬件和软件的资源限制问题

上下文切换

单核处理器也支持多线程执行代码,CPU 通过给每个线程分配 CPU 时间片来实现这个机制。时间片是 CPU 分配给各个线程的时间,因为时间片非常短,所以 CPU 通过不停地切换线程执行,让我们感觉多个线程是同时执行的,时间片一般是几十毫秒(ms)。


CPU 通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。所以任务从保存到再加载的过程就是一次上下文切换。


这个的意思就是告诉我们,并不是说线程越多越好,因为每个线程的使用 cpu 的时间是根据算法算出来的,也差不多算平均吧,如果你的线程太多,但是有很多线程是空闲状态,那你们你上下文切换的时间就是浪费的,这样还不如单线程执行呢,还有就是执行的次数,如果执行的次数少,那么串行也会比较快,有实验证明在 10W 循环执行中,2 者的速度差不多

我们如何去减少上下文的切换

减少上下文切换的方法有 无锁并发编程、CAS 算法、使用最少线程 和 使用协程。


  • 无锁并发编程。多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一些办法来避免使用锁,如将数据的 ID 按照 Hash 算法取模分段,不同的线程处理不同段的数据。

  • CAS 算法。Java 的 Atomic 包使用 CAS 算法来更新数据,而不需要加锁。

  • 使用最少线程。避免创建不需要的线程,比如任务很少,但是创建了很多线程来处理,这样会造成大量线程都处于等待状态。

  • 协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。

死锁

所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。 因此我们举个例子来描述,如果此时有一个线程 A,按照先锁 a 再获得锁 b 的的顺序获得锁,而在此同时又有另外一个线程 B,按照先锁 b 再锁 a 的顺序获得锁。如下图所示:



死锁产生的 4 个必要条件?


  • 互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。

  • 请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。

  • 不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。

  • 环路等待条件:在发生死锁时,必然存在一个进程--资源的环形链。


预防死锁:


  • 避免一个线程同时获取多个锁。

  • 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。

  • 尝试使用定时锁,使用 lock.tryLock(timeout)来替代使用内部锁机制。(锁超时)

  • 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。

资源限制的挑战

  • 硬件资源限制有带宽的上传/下载速度、硬盘读写速度和 CPU 的处理速度。

  • 软件资源限制有数据库的连接数和 socket 连接数等。

如何解决资源限制的问题

  • 对于硬件资源限制,可以考虑使用集群并行执行程序。比如使用 ODPS、Hadoop 或者自己搭建服务器集群,要么用阿里云的服务。

  • 对于软件资源限制,可以考虑使用资源池将资源复用。比如使用连接池将数据库和 Socket 连接复用,或者在调用对方 webservice 接口获取数据时,只建立一个连接。

本章总结

作者的建议是使用 JDK 并发包下的容器和工具类来解决并发问题,因为这些是已经经过了大量的测试,可以解决前面提到的几个挑战

结尾

第一章 介绍了并发的目的,和挑战,从全局大观上知道我们要学习的一个方向,继续加油吧。


因为博主也是一个开发萌新 我也是一边学一边写 我有个目标就是一周 二到三篇 希望能坚持个一年吧 希望各位大佬多提意见,让我多学习,一起进步。

日常求赞

好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是神人


创作不易,各位的支持和认可,就是我创作的最大动力,我们下篇文章见


六脉神剑 | 文 【原创】如果本篇博客有任何错误,请批评指教,不胜感激 !

发布于: 刚刚阅读数: 3
用户头像

自然

关注

还未添加个人签名 2020.03.01 加入

小六六,目前负责营收超百亿的支付中台

评论

发布
暂无评论
史上最全的Java并发系列之并发编程的挑战_多线程_自然_InfoQ写作社区