Fork me on GitHub

Java并发编程实战———显式锁

Lock与ReentrantLock

Lock提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁和解锁的方法都是显示的。

1
2
3
4
5
6
7
8
9
//Lock接口
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}

ReentrantLock实现了Lock接口,并提供了与synchronized相同的互斥性和内存可见性。

1
2
3
4
5
6
7
8
9
10
//使用ReentrantLock来保护对象状态
Lock lock = new ReentrantLock();
...
lock.lock();
try {
//更新对象状态
//捕获异常
} finally {
lock.unlock();
}

在synchronized和ReentrantLock之间进行选择

ReentrantLock在加锁和内存上提供的语义与内置锁相同,此外它还提供了一些其他功能,包括定时的锁等待、可中断的锁等待、公平性,以及实现非块结构的加锁。

ReentrantLock的危险性比同步机制要高,如果忘记在finally块中调用unlock,那么虽然代码表面上能正常运行,但实际上已经埋下隐患。

仅当内置锁不能满足需求时,才考虑使用ReentrantLock

在一些内置锁无法满足需求的情况下,ReentrantLock可以作为一种高级工具。当需要一些高级功能时才应该使用ReentrantLock,这些功能包括:可定时的、可轮询的与可中断的锁获取操作,公平队列,以及非块结构的锁。否则,还是应该优先使用synchronized

读-写锁

ReadWriteLock中暴露了两个Lock对象,其中一个用于读操作,而另一个用于写操作。要读取由ReadWriteLock保护的数据,必须首先获得读取锁,当需要修改ReadWriteLock保护的数据时,必须首先获得写入锁。

1
2
3
4
5
//ReadWriteLock接口
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}

在读-写锁实现的加锁策略中,允许多个读操作同时进行,但每次只允许一个写操作。

小结

与内置锁相比,显示的Lock提供了一些扩展功能,在处理锁的不可用性方面有着更高的灵活性,并且对队列有着更好的控制。但ReentrantLock不能完全替代synchronized,只有在synchronized无法满足需求时,才应该使用它。
读-写锁允许多个读线程并发地访问被保护的对象,当访问以读取操作为主的数据结构时,它能提高程序的可伸缩性。

求鼓励,求支持!