【Java并发编程系列9】锁
主要讲解Java中常见的锁。
前言
并发编程系列应该快接近尾声,锁可能是这个系列的最后一篇,重要的基本知识应该都涵盖了。然后对于书籍《Java并发编程实战》,最后面的几章,我也只看了锁的部分,这篇文章主要是对该书中锁的内容进行一个简单的总结。
死锁
死锁是指一组互相竞争资源的线程因互相等待,导致“永久”阻塞的现象。
锁顺序死锁
我们先看一个死锁的示例,我们先定义个BankAccount对象,来存储基本信息,代码如下:
public class BankAccount {
private int id;
private double balance;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
接下来,我们使用细粒度锁来尝试完成转账操作:
public class BankTransferDemo {
public void transfer(BankAccount sourceAccount, BankAccount targetAccount, double amount) {
synchronized(sourceAccount) {
synchronized(targetAccount) {
if (sourceAccount.getBalance() > amount) {
System.out.println("Start transfer.");
sourceAccount.setBalance(sourceAccount.getBalance() - amount);
targetAccount.setBalance(targetAccount.getBalance() + amount);
}
}
}
}
}
如果进行下述调用,就会产生死锁:
transfer(myAccount, yourAccount, 10);
transfer(yourAccount, myAccount, 10);
如果执行顺序不当,那么A可能获取myAccount的锁并等待yourAccount的锁,然而B此时持有yourAccount的锁,并正在等待myAccount的锁。
通过顺序来避免死锁
由于我们无法控制参数的顺序,如果要解决这个问题,必须定义锁的顺序,并在整个应用程序中按照这个顺序来获取锁。我们可以通过Object.hashCode返回的值,来定义锁的顺序:
public class BankTransferDemo {
private static final Object tieLock = new Object();
public void transfer(BankAccount sourceAccount, BankAccount targetAccount, double amount) {
int sourceHash = System.identityHashCode(sourceAccount);
int targetHash = System.identityHashCode(targetAccount);
if (sourceHash < targetHash) {
synchronized(sourceAccount) {
synchronized(targetAccount) {
if ...
回复