
如果在Java中使用多线程,可能会导致死锁问题. 死锁将使程序始终卡住,不再执行程序. 我们只能通过中止然后重新启动来重新启动程序.
僵局的原因可以概括为三句话:
首先,让我们看一下最简单的死锁(锁定顺序死锁)是如何发生的:
public class LeftRightDeadlock {
private final Object left = new Object();
private final Object right = new Object();
public void leftRight() {
// 得到left锁
synchronized (left) {
// 得到right锁
synchronized (right) {
doSomething();
}
}
}
public void rightLeft() {
// 得到right锁
synchronized (right) {
// 得到left锁
synchronized (left) {
doSomethingElse();
}
}
}
}
我们的线程交错执行,那么可能会发生以下情况:


让我们看看以下示例. 您认为会发生僵局吗?
// 转账
public static void transferMoney(Account fromAccount,
Account toAccount,
DollarAmount amount)
throws InsufficientFundsException {
// 锁定汇账账户
synchronized (fromAccount) {
// 锁定来账账户
synchronized (toAccount) {
// 判余额是否大于0
if (fromAccount.getBalance().compareTo(amount) < 0) {
throw new InsufficientFundsException();
} else {
// 汇账账户减钱
fromAccount.debit(amount);
// 来账账户增钱
toAccount.credit(amount);
}
}
}
}
上面的代码似乎没有问题: 在转移之前锁定两个帐户以确定余额是否足够!
但是,也可能发生死锁:
A:transferMoney(myAccount,yourAccount,10);
B:transferMoney(yourAccount,myAccount,20);
让我们看看以下示例:
public class CooperatingDeadlock {
// Warning: deadlock-prone!
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;
}
// setLocation 需要Taxi内置锁
public synchronized void setLocation(Point location) {
this.location = location;
if (location.equals(destination))
// 调用notifyAvailable()需要Dispatcher内置锁
dispatcher.notifyAvailable(this);
}
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);
}
// 调用getImage()需要Dispatcher内置锁
public synchronized Image getImage() {
Image image = new Image();
for (Taxi t : taxis)
// 调用getLocation()需要Taxi内置锁
image.drawMarker(t.getLocation());
return image;
}
}
class Image {
public void drawMarker(Point p) {
}
}
}

上面的getImage()和setLocation(点位置)都需要获取两个锁
这隐式获取了两个锁(对象之间的协作).
这种方式也很容易导致死锁...
有三种方法可以避免死锁:
上面的transferMoney()死锁的原因是因为锁定顺序不一致〜
然后可以将上面的示例转换如下:
public class InduceLockOrder {
// 额外的锁、避免两个对象hash值相等的情况(即使很少)
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);
}
}
}
// 得到锁的hash值
int fromHash = System.identityHashCode(fromAcct);
int toHash = System.identityHashCode(toAcct);
// 根据hash值来上锁
if (fromHash < toHash) {
synchronized (fromAcct) {
synchronized (toAcct) {
new Helper().transfer();
}
}
} else if (fromHash > toHash) {// 根据hash值来上锁
synchronized (toAcct) {
synchronized (fromAcct) {
new Helper().transfer();
}
}
} else {// 额外的锁、避免两个对象hash值相等的情况(即使很少)
synchronized (tieLock) {
synchronized (fromAcct) {
synchronized (toAcct) {
new Helper().transfer();
}
}
}
}
}
}

获取相应的哈希值以固定锁定顺序,这样就不会出现死锁的问题!
在协作对象之间的死锁示例中,主要是因为在调用一个方法时需要保持一个锁多线程如何避免死锁,并且在该方法内部还调用了其他带有锁的方法!
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-196408-1.html
光棍们就会找谁的麻烦
以前最起码吃
日本海自完全没有战斗机