- 思维导图
- 1 Lock和ReentrantLock
- 1.1 可轮询的和可定时的锁
- 1.2 可中断的锁获取操作
- 2 对性能的考量
- 3 公平性
- 4 在内部锁synchronized和ReentrantLock直接进行选择
- 5 读写锁
- 总结
- 参考文献
Lock定义了抽象的锁操作,如下图
Lock提供了无条件的、可中断的、可轮询的和定时的获取锁操作。
Lock的实现与内部锁具有相同的内存可见性语义。
ReentrantLock实现了Lock接口,提供了与synchronized相同的互斥和内存可见性保证。
内部锁存在的条件下,创建这种Lock机制原因,主要是内部锁具有的一些局限:
- 不能中断正在等待获取锁的线程,并且请求失败必须无限等待。
- 不具备高级特性,比如定时、可轮询等。
下列demo-1是Lock接口的规范形式:
private final Lock lock = new ReentrantLock();
//lock接口规范形式
public void addPlace(String place) {
lock.lock();
try {
//应用操作
//捕获异常
} catch (Exception e) {
//处理异常
} finally {
//finally释放锁
lock.unlock();
}
}
}
1.1 可轮询的和可定时的锁Lock锁必须在finally块中显示释放。
Lock定义的tryLock的两种形式可以实现可轮询和可定时的锁操作。这些操作可以避免死锁的发生。
- tryLock():立即返回,true表示获取成功,false表示锁不可用。
- tryLock(long time, TimeUnit unit):如当前线程没有被中断则获取锁,成功则返回true,否则当前线程将被休眠,直到发生下列事件:
- 1. 锁被当前线程获取。
- 2. 该线程被其它线程中断。
- 3. 指定的等待时间已过。
如demo-2演示了如何通过可轮询的方式,在指定时间进行一个操作:
public boolean transferMoney(Account from, Account to, BigDecimal amount, long timeout, TimeUnit timeUnit) throws InterruptedException {
long stopTime = System.nanoTime() + timeUnit.toNanos(timeout);
while (true) {
if (from.lock.tryLock()) {
try {
if (to.lock.tryLock()) {
try {
from.setBalance(from.getBalance().subtract(amount));
to.setBalance(to.getBalance().add(amount));
return true;
} finally {
to.lock.unlock();
}
}
} finally {
from.lock.unlock();
}
}
if (System.nanoTime() > stopTime) {
return false;
}
Thread.sleep(100);
}
}
上述代码通过传入一个等待时间,用于限定轮询获取锁的超时时间,如果超过这个时间还未获取两个锁,则代码直接失败返回false。
1.2 可中断的锁获取操作可中断的锁可以在取消活动中使用。
一个可中断的规范形式如demo-3:
public void sendMessage(String message) throws InterruptedException {
lock.lockInterruptibly();
try {
doSendMessage(message);
} finally {
lock.unlock();
}
}
当等待获取锁过程,被中断,方法将抛出中断异常,交给调用方处理。
2 对性能的考量ReentrantLock在Java5.0提供的竞争性要优于内部锁,但是Java6.0对内部锁进行了改善,使得两者性能差距非常小了。
性能是一个不断变化的目标。
3 公平性ReentrantLock提供了两种公平性选择。默认是非公平性锁:
两者区别在于非公平锁允许在首次获取时进行抢占,失败后仍然进行排队:
如果内部锁满足需求,不使用ReentrantLock,只有当你需要如下高级性能时使用:
- 可定时、可轮询和可中断的操作。
- 公平队列(内部锁非公平),或者非块结构的锁。
未来的性能改进倾向于synchronized,因为它是基于JVM的,能够进行优化。
5 读写锁读写锁:一个资源可以被多个读者访问,但是只能被一个写者访问。
ReadWriteLock接口定义如下:
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
只暴露了读锁和写锁。
如下等待demo-4是使用读写锁封装Map的示例:
public class ReadWriteMap{ private final Map map; private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private final Lock w = readWriteLock.writeLock(); private final Lock r = readWriteLock.readLock(); public ReadWriteMap(Map map) { this.map = map; } public V put(K k, V v) { //写锁控制写入-排它 w.lock(); try { return map.put(k, v); } finally { w.unlock(); } } public V get(K k) { //读锁控制读取-共享 r.lock(); try { return map.get(k); } finally { r.unlock(); } } }
读写操作前获取对应读写锁即可。
总结显式锁提供了内部锁的不具备的一些高级特性,但是不能完全替代内部锁。只有当你需要内部锁没有提供的特性时才使用显式锁。
参考文献[1]. 《JAVA并发编程实战》.



