这我们前面说了Java的集合,接下来湿兄给大家肝个Java并发系列,这个系列多么重要不用我多说了吧,这篇是个大家先来个简单的汇总篇,为的是让大家学习之前多并发有个全局意识。
在写之前我也是读了很多并发多线程相关的书籍,加上我的工作中的实战经验,来给大家分享这部分相关知识,如果文章有错的地方还请大家多多包涵,不吝留言给湿兄指正,感谢~~
学习并发对于初学者来说大概需要两个阶段,第一个阶段是先学习基础篇的多线程知识,等对多线程的使用有了初步的使用和基本掌握之后。第二个阶段需要进入多线程进阶篇,进阶篇需要了解的是JUC并发包中各个多线程工具类之间的关系和使用。
如果你跟着湿兄读到了本系列的最后,也就是Executor框架那里,相信你会明白为什么我要强调这个全局意识的重要性。
并发这块的话,难点不在于多线程程序有多复杂,而在于理清JUC包中的各个多线程工具类的之间的关系,并不是说必须要弄懂源码,深入源码确实能够让你掌握底层原理,但是过分深入源码可能会导致“当局者迷”。大家要掌握好属于自己的学习方法哦~
推荐几本并发方面经典的书籍,《Java并发编程实战》、《Java并发编程的艺术》、《Java多线程编程核心技术》,这几本也是湿兄主要用来学习和复习的,可以读一波的哦~
认识多线程
提起线程,不得不提起的是进程,这两个经常被用来做对比
进程我们应该很了解,打开windows任务管理器,可以发现运行着很多后台进程:
进程的定义:
-
进程是系统进行资源分配和调度的基本单位,是操作系统的基础
-
进程是程序的实体,例如QQ、微信都对应着相应的进程
-
进程是一个具有独立功能的程序关于某个数据集合的一次运行活动
线程的定义:
-
线程是系统进行资源分配和调度的最小单位
-
被包含在进程中,是进程的实际运作单位,一个进程可以并发多个线程
进程和线程的区别:
-
进程是操作系统资源分配基本单位,线程是操作系统资源分配最小单位
-
一个进程可以包含多个线程,一个进程中的多个线程可以共同完成
-
进程有自己的独立运行空间,程序切换进程开销大;线程可以看做轻量级进程,每个线程有自己的运行栈和PC程序计数器,线程切换开销小
-
一个进程崩溃后,在保护模式下不会对其它进程产生影响,但是一个线程崩溃会导致整个进程崩溃掉,多进程比多线程健壮
为什么系统有了进程,已经可以进行资源分配和调度了,为什么还要线程?
-
进程开销大,引入更小消耗的线程能充分利用CPU,便于系统管理
-
多线程可以给用户带来更好的用户体验,程序有更好的并发性
举个进程和线程的例子吧
QQ运行着就是一个进程,而其中的聊天、查看空间啊等这些又是在不同的线程上来操作的~
认识JUC并发包
上面的线程学习了之后,接下来就要学习JUC包中各个多线程工具类之间的关系、特点和使用场景
JUC并发包,即java.util.concurrent包,是JDK自1.5之后引入的核心工具包,由著名并发大师Doug Lea实现
整个java.util.concurrent包可以大致按照如下划分:
-
juc-locks锁框架
-
juc-atomic原子类框架
-
juc-sync同步类工具
-
juc-collection同步集合类
-
juc-executors执行器框架
我会按照上面的顺序来给大家分享相关的技术文章,给大家分享个不错的脑图
图片来源于mm.edrawsoft.cn/template/45…
juc-locks框架
在早期的JDK版本中,只提供了synchronized、wait、notify、notifyAll等比较底层的多线程同步工具,我们如果需要更复杂的多线程应用,一般需要基于JDK提供的这些基础工具进行自定义封装
JDK1.5之后,Doug Lea根据一些列常见的多线程设计模式,设计了这个JUC并发包提供了java.util.concurrent.locks这个包来对锁进行更多的补充和增强
java.util.concurrent.locks包的结构如下:
**包内的接口和类的继承图:
**
了解即可,接下来我会通过更多的文章来介绍
juc-atomic原子类
我们在之前JDK版本若要并发的对Integer、Long、Double等这些Java原始类或引用类型来进行操作,一般需要我们通过锁来控制并发,以防数据不一致
在JDK1.5之后的并发包中的java.util.concurrent.atomic工具包,这个包提供了Java原始/引用类型的映射类,如**AtomicInteger、AtomicLong、AtomicBoolean**,这些类的底层是通过一种无锁算法来实现的
无锁算法:乐观锁机制,其实底层就是通过Unsafe类实现的一种比较并且交换的算法,大致的过程是,当希望修改的值与expectedValue相同时,则尝试将值更新为updateValue,更新成功返回true,否则返回false。
java.util.concurrent.atomic包结构如下:
包的接口和类的结构图:
juc-sync同步类框架
我们同步器框架,指的是java.util.concurrent下的一些辅助同步器类,这些类都有一些使用场景:
同步器
作用
CountDownLatch
倒数计数器,构造时设定计数值,当计数值归零后,所有阻塞线程恢复执行;其内部实现了AQS框架
CyclicBarrier
循环栅栏,构造时设定等待线程数,当所有线程都到达栅栏后,栅栏放行;其内部通过ReentrantLock和Condition实现同步
Semaphore
信号量,类似于“令牌”,用于控制共享资源的访问数量;其内部实现了AQS框架
Exchanger
交换器,类似于双向栅栏,用于线程之间的配对和数据交换;其内部根据并发情况有“单槽交换”和“多槽交换”之分
Phaser
多阶段栅栏,相当于CyclicBarrier的升级版,可用于分阶段任务的并发控制执行;其内部比较复杂,支持树形结构,以减少并发带来的竞争
这几个工具类也是比较常用的,在面试中其实也是比较爱问的
juc-collection同步集合类
这是一些集合框架,是指的是java.util.concurrent包下的一些同步集合类,我们在线程安全的情况下经常会使用类似HashMap、ArrayList这些集合,这些集合都是线程不安全的,所以需要考虑使用环境
当多线程情况下,可以考虑使用这些同步集合类框架,按照类型划分为Map、List列表、Set集合、Queue队列四大类,每个类都有自己的使用场景
整个集合同步类架构图如下:
其中阻塞队列的分类及特性如下表:
队列特性
有界队列
近似无界队列
无界队列
特殊队列
有锁算法
ArrayBlockingQueue
LinkedBlockingQueue、LinkedBlockingDeque
/
PriorityBlockingQueue、DelayQueue
无锁算法
/
/
LinkedTransferQueue
SynchronousQueue
juc-executors执行器框架
executors框架可以说是整个包中最复杂的一部分了,也是最需要了解深度的,executors其实可以划分为三块,但是每一块的核心都是基于Executor接口:
-
线程池
-
Fork/Join框架
-
Future模式
线程池是用于使用多线程的情况下,用于减小经常创建和销毁线程这些造成的资源消耗,线程池会提供一些活跃线程供用户随时取用;Fork/Join框架类似于Hadoop框架的一种思想,拆分处理再合并的思想;Future则是多线程中常用的一种异步执行任务的机制,并在需要的时候取得结果
絮叨叨
你知道的越多,你不知道的也越多。
建议:拒绝白嫖,关注湿兄,快乐和学习并发~