Java并发相关组件或者技术包括:线程、线程池、阻塞队列、Future/FutureTask、Lock/Condition、Lock、AQS(队列同步器)、并发工具类、原子更新类、LockSupport、Unsafe等,下面我们以总体视角来看下这些组件之间的依赖关系。
image.png
Java线程池核心功能就是线程管理和任务存储,其底层基于阻塞队列来完成。线程池是日常开发中使用较多的并发组件,其带来的好处有:
- 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性:线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用线程池,必须了解其实现原理。
- 代码解耦:比如生产者消费者模式。
阻塞队列主要包括两部分内容,一个是存放数据的容器,另一个就是线程的管理(阻塞/唤醒),前者可以基于Array或者LinkedList数据结构,后者借助于Lock/Condition
来实现,也就是使用通知模式来实现的。
FutureTask用于在异步操作场景中,FutureTask作为生产者(执行FutureTask的线程)和消费者(获取FutureTask结果的线程)的桥梁,如果生产者先生产出了数据,那么消费者get时能会直接拿到结果;如果生产者还未产生数据,那么get时会一直阻塞或者超时阻塞,一直到生产者产生数据唤醒阻塞的消费者为止。关于Future/FutureTask可参考 FutureTask 原理剖析。
Lock/Condition是Java中提供的等待通知机制,使用Condition的await和signal,类似于基于synchronized的wait和notify,二者都可以实现等待通知机制。Condition的同步队列流程图:
关于Lock/Condition具体可查看 Lock Condition的那些事儿。
AQS(AbstractQueuedSynchronizer
,队列同步器)是构建JUC中锁和其他同步组件的基础组件,我们在日常开发中一般不会直接与AQS打交道。AQS核心功能就2点,通过CAS维护state状态,通过CAS维护同步队列进而控制线程的阻塞唤醒。换句话说就是:
AQS使用一个int成员变量(
private volatile int state
)表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作,并发包的作者(Doug Lea)期望它能够成为实现大部分同步需求的基础。
AQS是一个抽象类,但是并没有抽象方法,只不过有些方法是会抛出UnsupportedOperationException
,这些方法就需要根据不同场景进行子类重写,比如可重入锁NonReentrantLock
就重写了方法tryRelease、isHeldExclusively、tryAcquire
等,子类重写这些方法可自定义对应逻辑,比如判断是否可重入、是否公平设置state等,这块具体代码参考FairSync
、NonfairSync
类源码即可。
LockSupport定义了一组的公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能,而LockSupport也被称为构建同步组件的基础工具。LockSupport定义了一组以park开头的方法用来阻塞当前线程,以及unpark(Thread thread)方法来唤醒一个被阻塞的线程。LockSupport提供的阻塞和唤醒方法如下:
image.png
关于LockSupport更多资料可参考:为什么说LockSupport是Java并发的基石。
Java并发工具类主要有CyclicBarrier、CountDownLatch、Semaphore和Exchanger,日常开发中经常使用的是CountDownLatch和Semaphore。