引言
本文介绍了Java并发包中关于锁的组件以及相关API的使用方式,介绍的过程从两个角度展开:从使用角度上,通过示例演示了这些组件的使用方法以及部分API如何使用;从实现角度上,通过分析源码来剖析其实现的细节。通过以上两点做到使开发者在对锁的宏观使用层面和微观实现层面有一定了解。
金花鼠是松鼠家族中体形最小的成员,它们的祖先可以追溯到数百万年以前。只要有大量的种籽,有适于掘洞以保护它们不受众多捕食者伤害的土壤,它们几乎可以在任何地方生活。金花鼠的两颊内有两个富于弹性的袋子——颊袋。颊袋大得可以装进多达七个橡子,就像随身带着一个饭盒一样,它们可以把食物存在颊袋里,饿了,就拿出来吃一顿。金花鼠是极为爱清洁的动物,总是在不停地修饰自己。
接下来分别介绍以下锁组件及其API,具体介绍的内容如下表所示:
名称 | 简介 |
Lock | 锁接口,提供类似Java的synchronized关键字提供的同步功能,但不是类似关键字一样的隐式方式,而是显式的获取和释放锁。 |
AbstractQueuedSynchronizer | 一个基于FIFO队列的可以用于构建阻塞锁或者其他相关同步装置的基础框架。 |
ReentrantLock | 提供重进入的锁实现。 |
ReadWriteLock | 读写锁维护了一对锁,一个读锁和一个写锁,读锁能够同时的被多个读线程所获取,而写锁是一个排它锁。 |
LockSupport | 提供了线程阻塞的原语,如暂停和恢复等操作。 |
Condition | 完成类似java.lang.Object的监视器方法(wait、notify和notifyAll)的工作。 |
Java.util.concurrent.locks.Lock
简介
该接口提供类似Java的synchronized关键字提供的同步功能,但不是类似关键字一样的隐式方式,而是显式的获取和释放锁。
一个锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够排他的防止多个线程同时访问共享资源( 但是有些锁可以允许多个线程并发的访问共享资源,比如读写锁)。
使用synchronized关键字将会隐式的获取锁,但是它将锁的获取和释放固化了,也就是先获取再释放,当然这种方式简化了同步的管理,但是扩展性没有显示的锁获取和释放来的好。比如针对一个场景,手把手的锁获取和释放,先获得锁A,然后再获取锁B,当锁B获取到后,再释放锁A同时获取锁C,再释放B同时获取锁D。这种形式下使用显示的Lock将会有很好的扩展性,实现起来也容易一些。
锁的使用也很简单,以下是锁的使用的模式:
Lock lock = new ReentrantLock();
lock.lock();
try {
} finally {
lock.unlock();
}
需要确保锁在获取到之后,最终能够被释放,不要将lock写在try中,因为如果lock在锁时发生了异常,将会导致锁无故的释放。
Lock接口提供了超越synchronized关键字的诸多特性,比如:
1. 非阻塞的tryLock
将会尝试获取锁,不管获取与否,立刻返回获取结果true or false;
2. 能被中断的lockInterruptibly
在获取锁时,当前线程能够感知到interrupt(),这个在synchronized块中,是无法打断的(同步I/O也一样);
3. 有超时的获取锁tryLock(long, TimeUnit)
在指定的之间没有获取锁,则自动退出,返回false。
API说明
下面介绍一下Lock接口的API,之后待介绍完同步器AbstractQueuedSynchronizer之后再介绍一下常用的锁实现ReentrantLock,因为锁的实现基本都是在实现接口的前提下,聚合了一个AbstractQueuedSynchronizer的子类来完成线程访问控制的。
方法名称 | 描述 |
void lock() | 获取锁。 如果当前线程没有获取到锁,那么该线程将会从线程调度上摘下来,直到锁被获取到为止。 |
void lockInterruptibly() throws InterruptedException | 当前线程没有被中断的情况下获取锁。 如果获取到锁后,直接返回,否则如果lock一样,将会从线程调用上摘下来,但是如果在休眠过程中,该线程被中断,interrupt,那么将会直接抛出InterruptedException,这是一种可以响应中断的锁方式。 |
boolean tryLock() | 尝试获取锁,如果能够获取则返回true,否则立刻返回false。 |
boolean tryLock(long time, TimeUnit unit) throws InterruptedException | 类似lockInterruptibly一样获取锁。 如果能够获取到则立刻返回,否则当前线程休眠,在三种情况下会返回: 1.当前线程获取到了锁; 2.当前线程被中断,抛出InterruptedException; 3.超时时间结束,然后返回false。 |
void unlock() | 释放锁。 |
Condition newCondition() | 获取Condition,类似wait/notify的组件。 该Condition和当前的Lock绑定在一起,只有获取了锁,才能调用Condition的wait方法,而调用后,当前线程将失去锁。 |