引言
JUC即java.util.concurrent,是java提供的用于多线程处理的工具类库。重点关注 ConcurrentXXX、AtomicXXX、Executor、Caller&&Future、Queue、Locks。
打开jdk包,JUC代码就在rt.jar包中,concurrent下有atomic和locks两个package,并且还有concurrentXXX、Executor、Future、Queue等。想查看源码的话,JDK 安装的目录下有个 src.zip —— 这就是源码。
AtomicXXX,atomic即原子类,如图:
常用工具类及其作用
1、基本类型的原子类
AtomicBoolean,AtomicInteger,AtomicLong,DoubleAccumulator,DoubleAdder,LongAccumulator,LongAdder 都是基本数据类型的原子类,更适合高并发场景。
以 AtomicLong 和 LongAdder 来说,AtomicLong 是通过 unsafe 类实现的,基于 CAS。unsafe 类是底层工具类,JUC 中很多类的底层都使用到了 unsafe 包中的功能。unsafe 类提供了类似 C 的指针操作,提供 CAS 等功能。并且 unsafe 类中的所有方法都是 native 修饰的。在说 AtomicXXX 之前,已经说过了关于 CAS的相关知识,并发与多线程【四】——同步与互斥。
LongAdder、DoubleAccumulator,DoubleAdder,LongAccumulator 四个类是 JDK1.8 中提供的更高效的操作类。LongAdder 基于 Cell 实现,使用分段锁思想,是一种空间换时间的策略,更适合高并发的场景。
LongAccumulator 提供了比 LongAdder 更强大的功能,能够指定对数据的操作规则,例如可以把数据的相加操作改为相乘操作。
2、对对象的原子读写功能的类
AtomicReference、AtomicStampedReference、AtomicMarkableReference 类中提供了对对象的原子读写功能,后两个类用于解决前面说过的 ABA 问题,分别基于时间戳和标记位来解决问题。
3、锁相关的类
锁相关的类,ReentrantLock、ReentrantReadWriteLock、StampedLock、LockSupport。
ReentrantLock 前面已经说过是重入锁。与 ReentrantLock 独占锁不同的是,Sempaphore 是共享锁,允许多个线程共享资源,适用于限制使用共享资源线程数量的场景,例如100辆车要使用20个停车位,那么最多允许20个车占用停车位。
StampedLock是 JDK1.8 改进的读写锁,是使用一种 CLH 的乐观锁,能够有效防止写饥饿。所谓写饥饿是指在多线程读写时,读线程访问非常频繁,导致总是有读线程占用资源,写线程很难加上写锁。
4、异步执行相关的类
Executors、ForkJoinPool、FutureTask、CompletableFuture是异步执行相关的类。
JDK1.8中提供的 CompletableFuture 可以支持流试调用,可以很方便的进行多 future 的组合使用,例如可以同时执行两个异步任务,然后对执行结果进行合并处理,还可以很方便的设置完成时间。
JDK1.7中提供的 ForkJoinPool 采用分治思想,将大任务分解成多个小任务处理,然后再合并处理结果。ForkJoinPool 的特点是使用工作窃取算法,可以有效平衡多任务时间长短不一的场景。
5、常用的阻塞队列
线程池讲解的时候已经简单介绍过阻塞队列,这里补充一下LinkedBlockingDeque、ArrayBlockingQueue。
LinkedBlockingDeque 是双端队列,也就是可以分别从队头和队尾操作入队、出队。
ArrayBlockingQueue 是单端队列,只能从队尾入队,队头出队。
6、控制多线程协作时使用的类
CountdownLatch、CyclicBarrier、Semaphore 是控制多线程协作时使用的类。
CountdownLatch 实现计数器功能,可以用来控制等待多个线程执行任务后进行汇总。
CyclicBarrier 可以让一组线程等待至某个状态之后,再同时执行,一般在测试时使用,可以让多线程更好的并发执行。
Semaphore 用来控制对共享资源的访问并发度。
7、常用的集合类
ConcurrentHashMap、CopyOnWriteArrayList是常用的两个集合类。
ConcurrentHashMap 可以拉出来一章单独讲。
这里了解一下 CopyOnWriteArrayList,COW 通过写入数据时进行 copy 修改,然后更新引用的方式,来消除并行读写中的锁使用,比较适合读多写少,数据量比较小,但是并发非常高的场景。
扩展
- https://blog.csdn.net/a953713428/article/details/54384166