前言
文本已收录至我的GitHub仓库,欢迎Star:github.com/bin39232820…
种一棵树最好的时间是十年前,其次是现在
絮叨
从今天开始,我们进入一个新的领域学习,这个领域的学习,我打算跟着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并发包下的容器和工具类来解决并发问题,因为这些是已经经过了大量的测试,可以解决前面提到的几个挑战
结尾
第一章 介绍了并发的目的,和挑战,从全局大观上知道我们要学习的一个方向,继续加油吧。
因为博主也是一个开发萌新 我也是一边学一边写 我有个目标就是一周 二到三篇 希望能坚持个一年吧 希望各位大佬多提意见,让我多学习,一起进步。