Java线程间通信与同步可以分为以数据为中心和以流程为中心两种方式。
volatile
volatile关键字可以施加在变量的定义上,保证该变量值的可见性,是线程间通信的初级方式。
给JVM的语义是,该变量的值是不确定的,线程在使用该值时,都应该读取或写入到主内存中,而不能从寄存器中读取。
synchronized
对volatile语义进行了加强,既能施加在变量上,也能施加在方法或代码块上。
施加在变量上时(不是变量声明处,而是变量使用时),保证该变量值的可见性,也能保证对该变量的原子性操作。
施加在方法上时,就属于同步语义了,施加在代码块上与施加在方法上类似,只是同步粒度减小了。
synchronized的实现使用锁实现,有可能造成死锁。
ReentranceLock
可重入锁是对synchronized的增强,增加了超时、可中断等功能。
ReadWriteLock
读写锁是对ReentranceLock锁粒度细化的结果。在实际应用中,我们总可以将访问数据的线程分为读和写两种线程,读线程之间无需同步,只要做好读与写、写与写之间的同步就够了。故而读写锁通过降低锁粒度,放开读的同步操作,能够达到提高并发的效果。
Condition
Condition也是对ReentranceLock锁粒度细化的产物。在实际应用中,我们发现能够将对数据操作的线程进行分类,在唤醒阻塞线程时,每次唤醒某个或某些类型的线程,而不是每次都唤醒全部线程,会对性能有很好的帮助(避免羊群效应)。
Condition是配合ReentranceLock使用的,跟synchronized与wait/notify类似
Semaphone
Semaphone放宽了可重入锁的语义,允许同时有指定个数的线程同时访问数据。如果指定访问个数等于1,就退化为了可重入锁,所以,可重入锁可以看成是一种特殊的信号量。
上面的几种方式都是以数据为中心而进行的通信与同步方式,下面两种是以流程为中心的两种同步方式。
CountDownLatch
开启n个线程,在这些线程全部完成前阻塞,n个线程全部完成后,继续执行,CountDownLatch就是实现了这种功能。
CyclicBarrier
n个线程,分为m个阶段,阶段内可以并行,但每个阶段终点必须同步,当全部线程都达到k阶段的终点时,才统一开始k+1阶段的工作。CyclicBarrier就是实现了这种功能。