基础同步工具类

Semaphore,CountDownLatch,CyclicBarrier 均是 JDK1.5 提供的基础并发工具:

  • Semaphore 是一个计数信号量,用于限制同时访问某个特定资源的数量
  • CountDownLatch 是一个闭锁,允许一个或多个线程等待一组其他线程执行完成后执行,但只能使用一次
  • CyclicBarrier 是一个循环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行,并且支持重复使用

Semaphore

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class X{

Random random = new Random();

Semaphore semaphore = new Semaphore(10, true);

// 方法本身控制同步
public synchronized Integer getNextAvailableItem() throws InterruptedException {

// 信号量控制访问次数
semaphore.acquire();

return random.nextInt();
}

public synchronized boolean markAsUnused(Integer item){

// do something

semaphore.release();

return true;
}
}

这是 Semaphore 的一个标准的使用方式,用于控制流量。上述程序创建了一个允许 10 个线程同时访问的信号量,并且使用公平锁(一般来说用于控制流量的使用需要使用公平模式,用于防止线程饥饿),然后在提供获取资源的接口 getNextAvailableItem 方法前先获取凭证,在释放资源后释放凭证。但是注意 Semaphore 不保证并发正确性,这需要接口自己保证,因此这里使用 synchronized 来提醒这一点。

ReentrantLock

  ReentrantLock 是基于 AQS 同步器实现的互斥锁,它支持设置公平锁/非公平锁模式,同时具有可重入性。在这里讨论 ReentrantLock 对这些特性的支持及应用。

标准模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class X {

private final ReentrantLock lock = new ReentrantLock();

public void m() {

lock.lock();

try {

// ... method body

} finally {

lock.unlock();
}
}

}

AQS同步器

  在 java.util.concurrent (JUC) 并发包中,如 ReentrantLock,Semaphore,CountDownLatch 等并发类的同步控制都是基于 AbstractQueuedSynchronizer (简称 AQS) 这个同步器抽象类来实现的。在这里较为深入的讨论同步器抽象类的实现原理与应用。

AQS简介

AbstractQueuedSynchronizer 内部维护着一个 FIFO 的 CLH 队列,队列中的每个 Node 代表着一个需要获取锁的线程

  自旋锁:自旋锁是指当一个线程尝试获取某个锁时,如果该锁已被其他线程占用,就一直循环检测锁是否被释放,而不是立刻进入线程挂起或睡眠状态。

  • CLH 锁(Craig, Landin, and Hagersten locks):基于链表的可扩展、高性能、公平的自旋锁,它不断轮询前驱的状态,如果发现前驱释放了锁就结束自旋
  • MCS 锁:在当前结点自旋,但由前驱结点通知其结束自旋

AQS 采用的是一种变种的 CLH 队列锁:原始 CLH 是在前驱结点自旋,通过判断 pred.locked 来自旋,而 AQS 的 CLH 则是根据前驱结点的状态来控制阻塞,不会一直自旋。同时当前驱结点释放锁时会去唤醒该结点使其参与竞争锁。 AQS 的结点的定义如下:

内存屏障

  现代计算机大多数采用多核处理器或多处理器以提高性能,同时每个处理器通常存在一层或多层高速缓存,这将会更进一步加快对数据的访问。但是这也带来了新的挑战,即同一数据在不同处理器之间并不保证一致。所以为了保证数据的可见性,内存屏障应运而生。它能够刷新或使本地处理器高速缓存失效,以便查看其他处理器进行的写入的最新值或使该处理器的写入对其他处理器可见。而 Java 内存模型用于屏蔽不同硬件所带来的内存访问差异,以实现程序在不同平台能保证一致的并发效果。那么在并发环境下如何正确的使用内存屏障成为了首要问题。

硬件内存架构

Java 内存模型是对硬件内存模型的抽象,因此理解它需要一些硬件基础知识。这里主要介绍 处理器的存储器结构处理器的高速缓存与缓存一致性

随机访问存储器(RAM)

随机访问存储器(RAM),是用于和 CPU 交换数据的内部存储器。根据存储单元的工作原理不同可以分为两类:

  • 动态的存储器(DRAM)
  • 静态的存储器(SRAM)

SRAM 比 DRAM 更快,但也更贵,因此 SRAM 主要作为高速缓存存储器(Cache,如 CPU 的 L1,L2),而 DRAM 则作为计算机主存。而我们常说的内存指的就是是计算机的主内存 DRAM。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×