导图社区 JUC
Java并发包内容思维导图,本图汇总了CAS基础、Atomic包、locks包、线程池、对比开源框架、常用并发工具、AQS、并发容器的内容,大家可以学起来哦。
编辑于2023-02-20 10:32:32 浙江省JUC
CAS基础
cas全称compare and swap,其理论思想类似于乐观锁,但是无状态(ABA问题存在)
绝大多数的cpu支持cas指令,编程时可以认为cas就是原子操作
如果程序不能容忍ABA问题,则不可使用cas算法
Unsafe类是完成CAS操作的基础类,但是JVM开发者并不希望程序开发者使用
Atomic包
为了让用户享受cas带来的收益,jdk广泛了使用了CAS算法, atomic则是提供给程序员使用的一些原子类,提供能保证线程安全性的一些常用操作
AtomicInteger
对比Integer,它是可变的,而且是线程安全的
对于一些赋值操作,则是死循环的使用CAS指令,毕竟CAS指令并非一定成功
常见操作有addAndGet,updateAndGet,compareAndSet等等
几乎一样的类还有AtomicBoolean,AtomicLong
AtomicReference
修改引用的原子类,可用于复杂类型的
AtomicStampReference
相对于AtomicReference,可以解决ABA问题,相当于加入了状态值
AtomicIntegerArray
对整型数组的操作
AtomicIntegerFiledUpdater
对int属性的原子操作
属性必须是int类型,不能使用包装类型Integer
只能是实例属性,不能是类属性
必须用volatile修饰
必须是可访问的属性
locks包
ReentrantLock
重入锁,算是synchronized关键字的扩展,在JDK1.6之后两者性能并无明显差别
与synchronized的区别
支持跨方法的锁定与释放
支持超时返回,和尝试获取锁(有效避免死锁)
支持响应中断
支持锁的公平和非公平选择
支持多条件(condition)的通知策略
ReentrantReadWirteLock
在Reentrant的基础上做了读写分离,适用于高频读的场景
实现原理中的用一个int值表示读和写两种状态非常巧妙,值得探索
StampedLock
在JDK1.8之后加入的锁,其补充读写锁带来的读写互斥问题
支持乐观读,只有写锁改变了状态才会影响读锁
LockSupport
通过静态方法,对线程挂起和恢复的工具类
线程池
ThreadPoolExecutor
构造器入参
corePoolSize(int)始终维护的线程个数
maxPoolSize(int)线程池可扩展的最大线程数量
keepAliveTime(int)超corePoolSize的线程恢复到corePoolSize的可空闲时间,配合④中的单位,确定时间
unit(TimeUnit)③中的单位
workQueue(BlockingQueue)任务缓冲队列
threadFactory(ThreadFactory)线程工厂
handler(RejectedExecutionHandler)拒绝策略
任务提交过程
否,创建线程执行任务
是,执行第二步
判断当前线程是否大于等于corePoolSize
否,创建线程执行任务
是,执行第二步
任务是否可以加入workQueue
是,加入workQueue
否,执行第三步
判断当前线程是否大于或等于maxPoolSize
否,创建线程执行任务
是,执行拒绝策略
实现原理
内部使用Worker类实现,worker实现了AQS,自己本身是一把锁
Worker不断从workQueue队列中获取任务,如果获取不到,则被阻塞
shutDown时,调用用worker的tryLock,如果不能则worker繁忙
一个原子整型类, 前3位维护线程状态,后29位维护线程数量
ScheduledThreadPoolExecutor
继承ThreadPoolExecutor,内部使用DelayedQueue时间调度方案
ForkJoinPool
分治思想,fork代表拆分任务,join代表合并结果
RecursiveAction/RecursiveTask实现ForkJoinTask,分别提供无返回值和有返回值的compute方法,递归方法的实现
为了避免fork是滥用线程,所以使用ForkJoinPool来提交ForkJoinTask
Executors
简单理解为线程池工厂类
newSingleThreadExecutor,创建一个单一线程的线程池,workQueue为无界队列
newFixedThreadPool,创建一个固定数量的线程池,workQueue为无界队列
newCachedThreadPool,创建一个任意线程数量的线程池,workQueue无容量,corePoolSize为0
newSingleThreadScheduledExecutor,创建一个单一任务调度线程池
newScheduledThreadPool,创建固定数量任务调度的线程池
newWorkStealingPool,创建一个ForkJoinPool线程池,抢占式线程池
对比开源框架
Guava
RateLimiter, 限流工具类
DirectExecutor,直接在提交线程执行的线程池
Daemon线程池,MoreExecutors.getExitingExecutorService()
Future中加入FutureCallBack能在任务调度完成后回调
AKKA
是个高并发的事件驱动框架,性能极高,可以了解
常用并发工具
Semaphore
对锁的一种补充,能允许多个线程进入临界资源
只控制线程数量,并不保证线程安全
CountDownLatch
线程倒计数器,等到计数到达某个值时出发
助理解:比如计数器是10,多个线程完成任务后将计数器-1,主线程等到计数器为0时触发
CyclicBarrier
相当于是可以循环使用的计数器了,每次到达数量触发一次await()方法返回
Exchanger
线程间的数据交换,倒是不太常用
ThreadLocal
这个并非是JUC下面的类,但是应用广泛,理解上放一起也行
主要作用是给每个线程一个副本对象,多线程环境下,每个ThreadLocal封装的对象都是线程独有的,而且不会被其他线程访问到
ThreadLocalRandom
JDK1.7之后的随机数工具
其目的是解决多线程下Random随机数的实例争夺问题
Future/Callable
将任务交给另外一个线程执行,可以获得对应的返回结果,常用语异步编程
理解中,要明确任务只是被另外一个线程执行,缩短的提交线程的同步等待,并非可以提高系统的处理能力
AQS
AbstractQueuedSynchronizer单独讲是因为它很重要,很多工具都是基于它实现的
自己或内部类实现AQS的工具有ReentrantLock,ReentrantReadWriteLock,Semaphore,CountDownLatch等
实现锁需要的条件
需要一个状态记录是否上锁或锁次数,由AQS定义的int类型的state
需要一个记录当前持有锁的线程,由AQS定义
需要一个对线程挂起,恢复的工具,用LockSupport完成的
需要将等待锁的线程加入一个队列进行维护,由AQS完成
通过锁需要的条件,可以很明显看出AQS的作用了,就是维护锁状态,持有锁线程和等待锁的队列
并发容器
BlockingQueue
添加元素
add,队列满时抛出异常,成功返回true
offer,返回添加成功或者失败
put,队列满时阻塞住,没有返回值
移除元素
remove删除指定的元素不阻塞
poll从队列头移除一个元素,不阻塞
peek获取队列头元素,不会移除元素,不阻塞
take阻塞
LinkedBlockingQueue
内部使用链表实现的一个BlockingQueue
ArrayBlockingQueue
内部是用数组实现的一个BlockingQueue
PriorityBlockingQueue
可以根据元素大小进行优先级出队列,实现comparable接口
DelayQueue
延时队列,可以获取剩余时间,如果时间小于等于0,可以将其取出
由Delayed接口的实现来计算时间
SynchronousQueue
无容量队列,put后需要另外一个线程take才会双方解锁
LinkedBlockingDeque
这是一个双端队列,也是唯一的双端队列
CopyOnWriteArrayList
数组实现的写时复制列表容器
CopyOnWriteArraySet
数组实现的写时复制set集合
ConcurrentLinkedQueue/Deque
并发的链表队列,是一个单向链表
在该队列中,head/tail总是迟于节点的入队和出队
ConcurrentHashMap
非CAS实现的容器,JDK1.7及之前使用多个Segment,以分段锁的形式保证并发
JDK1.8之后,去掉了分段,以红黑树的方式代替,锁住的是每个链表的头部
好处是减少hash碰撞中的性能消耗,红黑树比链表性能更高的查询,并发度更高
ConcurrentSkipListMap/Set
跳表的map和set,用CAS实现