一、显式锁
- 简介
- Lock 提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁和解锁的方法都是显式的。
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
- Lock 并不是一种替代内置加锁的方法,而是当内置加锁机制不适用时,作为一种可选择的高级功能。
- 内置锁无法中断一个正在等待获取锁的线程,无法实现非阻塞结构的加锁规则。
- Lock 提供与 synchronized 相同的互斥性和内存可见性、可重入、更灵活,但需要手动加锁与释放:
Lock lock = new ReentrantLock();
...
lock.lock();
try {
// 更新对象状态,捕获异常并在必要时恢复不变性条件
} finally {
// 程序的执行控制离开被保护的代码块时不会自动清除锁,切记在 finally 中释放 Lock
lock.unlock();
}
1.1 轮询锁
通过 tryLock 实现:如果不能获得所有需要的锁,释放已经获得锁,然后重新尝试获取所有锁。
public boolean transferMoney(Account fromAcct, Account toAcct, DollarAmount amount,
long timeout, TimeUnit unit)
throws InsufficientFundsExeception, InterruptedException {
long fixedDelay = getFixedDelayComponentNanos(timeout, unit);
long randMod = getRandomDelayModulusNanos(timeout, unit);
long stopTime = System.nanoTime() + unit.toNanos(timeout);
while (true) {
if (fromAcct.lock.tryLock()) {
try {
if (toAcct.lock.tryLock()) {
try {
if (fromAcct.getBalance().compareTo(amount) < 0) {
throw new InsufficientFundsExeception();
} else {
fromAcct.debit(amount);
toAcct.credit(amount);
return true;
}
} finally {
toAcct.lock.unlock();
}
}
} finally {
fromAcct.lock.unlock();
}
}
if (System.nanoTime() < stopTime)
return false;
NANOSECONDS.sleep(fixedDelay + rnd.nextLong() % randMod);
}
}
1.2 定时锁
通过 tryLock 实现:如果操作不能在指定的时间内给出结果,那么就会使程序提前结束。
public boolean trySendOnSharedLine(String message, long timeout, TimeUnit unit)
throws InterruptedException {
long nanosToLock = unit.toNanos(timeout) - estimatedNanosToSend(message);
if (!lock.tryLock(nanosToLock, NANOSECONDS))
return false;
try {
return sendOnSharedLine(message);
} finally {
lock.unlock();
}
}
1.3 中断锁
Lock 类中提供了 lockInterruptibly方法,该方法允许在等待一个锁的同时仍能响应中断。
public class InterruptibleLocking {
private Lock lock = new ReentrantLock();
public boolean sendOnSharedLine(String message)
throws InterruptedException {
lock.lockInterruptibly();
try {
return cancellableSendOnSharedLine(message);
} finally {
lock.unlock();
}
}
private boolean cancellableSendOnSharedLine(String message) throws InterruptedException {
/* send something */
return true;
}
}
1.4 公平锁
通过
new ReentrantLock(true)
实现,默认创建的都是非公平锁。
- 在公平的锁上,线程将按照它们发出请求的顺序来获得锁。
- 在非公平的锁上,如果发出请求的同时该锁的状态变为可用,那么这个线程将跳过队列中所有等待的线程并获得这个锁。
- 线程挂起和恢复的开销很大,无需必要请使用默认的非公平锁:
1.5 读写锁
互斥是一种过于保守的、强硬的加锁策略,有时会不必要的限制并发性。
- 读写锁的策略是:允许多个读操作同时进行,但每次只允许一个写操作。
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
public class ReadWriteMap<K, V> {
private final Map<K, V> map;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock r = lock.readLock();
private final Lock w = lock.writeLock();
public ReadWriteMap(Map<K, V> map) {
this.map = map;
}
public V put(K key, V value) {
w.lock();
try {
return map.put(key, value);
} finally {
w.unlock();
}
}
public V get(K key) {
r.lock();
try {
return map.get(key);
} finally {
r.unlock();
}
}
}
二、死锁
2.1 锁顺序死锁
- 问题引出
public static void transferMoney(Account fromAccount,
Account toAccount,
DollarAmount amount)
throws InsufficientFundsException {
synchronized (fromAccount) {
synchronized (toAccount) {
if (fromAccount.getBalance().compareTo(amount) < 0)
throw new InsufficientFundsException();
else {
fromAccount.debit(amount);
toAccount.credit(amount);
}
}
}
}
- 问题解决:保证获取锁的顺序
public class InduceLockOrder {
private static final Object tieLock = new Object();
public void transferMoney(final Account fromAcct, final Account toAcct, final DollarAmount amount)
throws InsufficientFundsException {
class Helper {
public void transfer() throws InsufficientFundsException {
if (fromAcct.getBalance().compareTo(amount) < 0)
throw new InsufficientFundsException();
else {
fromAcct.debit(amount);
toAcct.credit(amount);
}
}
}
int fromHash = System.identityHashCode(fromAcct);
int toHash = System.identityHashCode(toAcct);
if (fromHash < toHash) {
synchronized (fromAcct) {
synchronized (toAcct) {
new Helper().transfer();
}
}
} else if (fromHash > toHash) {
synchronized (toAcct) {
synchronized (fromAcct) {
new Helper().transfer();
}
}
} else {
synchronized (tieLock) {
synchronized (fromAcct) {
synchronized (toAcct) {
new Helper().transfer();
}
}
}
}
}
}
2.2 协作对象死锁
如果在持有锁时调用某个外部方法,那么将出现活跃性问题。在这个外部方法中可能会获取到其它锁(这可能产生死锁),或者阻塞时间过长,导致其它线程无法及时获得当前被持有的锁。
- 问题引出
public class CooperatingDeadlock {
class Taxi {
@GuardedBy("this") private Point location, destination;
private final Dispatcher dispatcher;
public Taxi(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
}
public synchronized Point getLocation() {
return location;
}
// 先尝试获取 Taxi 锁
public synchronized void setLocation(Point location) {
this.location = location;
if (location.equals(destination))
dispatcher.notifyAvailable(this); // 再尝试获取 Dispatcher 锁
}
public synchronized Point getDestination() {
return destination;
}
public synchronized void setDestination(Point destination) {
this.destination = destination;
}
}
class Dispatcher {
@GuardedBy("this") private final Set<Taxi> taxis;
@GuardedBy("this") private final Set<Taxi> availableTaxis;
public Dispatcher() {
taxis = new HashSet<Taxi>();
availableTaxis = new HashSet<Taxi>();
}
public synchronized void notifyAvailable(Taxi taxi) {
availableTaxis.add(taxi);
}
// 先尝试获取 Dispatcher 锁
public synchronized Image getImage() {
Image image = new Image();
for (Taxi t : taxis)
image.drawMarker(t.getLocation()); // 再尝试获取 Taxi 锁
return image;
}
}
}
- 开放调用:如果在调用某个方法时不需要持有锁,那么这种调用称为开放调用。
class CooperatingNoDeadlock {
@ThreadSafe
class Taxi {
@GuardedBy("this") private Point location, destination;
private final Dispatcher dispatcher;
public Taxi(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
}
public synchronized Point getLocation() {
return location;
}
public synchronized void setLocation(Point location) {
boolean reachedDestination;
synchronized (this) { // 减小锁的范围
this.location = location;
reachedDestination = location.equals(destination);
}
if (reachedDestination)
dispatcher.notifyAvailable(this);
}
public synchronized Point getDestination() {
return destination;
}
public synchronized void setDestination(Point destination) {
this.destination = destination;
}
}
@ThreadSafe
class Dispatcher {
@GuardedBy("this") private final Set<Taxi> taxis;
@GuardedBy("this") private final Set<Taxi> availableTaxis;
public Dispatcher() {
taxis = new HashSet<Taxi>();
availableTaxis = new HashSet<Taxi>();
}
public synchronized void notifyAvailable(Taxi taxi) {
availableTaxis.add(taxi);
}
public Image getImage() {
Set<Taxi> copy;
synchronized (this) { // 减小锁的范围
copy = new HashSet<Taxi>(taxis);
}
Image image = new Image();
for (Taxi t : copy)
image.drawMarker(t.getLocation());
return image;
}
}
}