并发主题

什么时候使用Reentrant锁?

Java 5.0提供Reentrant Lock. 相比‘synchronized’ 和 ‘volatile’,能更好完成并发

之前我们使用下面代码:

public synchronized void doAtomicTransfer(){
//enter synchronized block , acquire lock over this object.
operation1()
operation2();
} // exiting synchronized block, release lock over this object.

使用同步锁容易引起死锁。以下面transferMoneyWithSync转账为案例为例子:

public void transferMoneyWithSync(Account fromAccount, Account toAccount,
float amount) throws InsufficientAmountException {

synchronized (fromAccount) {
// acquired lock on fromAccount Object
synchronized (toAccount) {
// acquired lock on toAccount Object
if (amount > fromAccount.getCurrentAmount()) {
throw new InsufficientAmountException(
"Insufficient Balance");
} else {
fromAccount.debit(amount);
toAccount.credit(amount);
}
}
}
}

当出现:

A: transferMoney(acc1, acc2, 20);
B: transferMoney(acc2, acc1 ,25);

两个线程陷入互相等待的死锁当中。

ReentrantLock有下面几个方法:

void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
.....

使用tryLock能够加入时间日期后能够避免这种死锁等待,这种方法被称为"定时查询锁收购"。它可以让你重新获得控制权,如果你不能获得必需的锁,释放你现在已经获得锁,然后重试。

下面使用一个有时间的锁:

public boolean transferMoneyWithTryLock(Account fromAccount,
Account toAccount, float amount) throws InsufficientAmountException, InterruptedException {

// we are defining a stopTime
long stopTime = System.nanoTime() + 5000;
while (true) {
if (fromAccount.lock.tryLock()) {
try {
if (toAccount.lock.tryLock()) {
try {
if (amount > fromAccount.getCurrentAmount()) {
throw new InsufficientAmountException(
"Insufficient Balance");
} else {
fromAccount.debit(amount);
toAccount.credit(amount);
}

} finally {
toAccount.lock.unlock();
}
}

} finally {
fromAccount.lock.unlock();
}
}
if(System.nanoTime() < stopTime)
return false;

Thread.sleep(100);
}//while
}

如果锁时间过5000ms没有释放,将返回出错。如果锁在指定时间不能被得到,transferMoney方法将返回一个失败通知,并退出。

中断锁收购

lockInterruptibly的方法:收购中断锁允许锁在被撤销活动中再次使用。

假设我们共享Line发送消息,如果另一个线程来中断当前线程应当释放锁,并执行退出或关闭操作来取消当前任务。

public boolean sendOnSharedLine(String message) throws InterruptedException{
lock.lockInterruptibly();
try{
return cancellableSendOnSharedLine(message);
} finally {
lock.unlock();
}
}

private boolean cancellableSendOnSharedLine(String message){