1、死锁
(1)锁顺序死锁
例子:
public class LeftRightDeadlock {
private final Object left = new Object();
private final Object right = new Object();
public void leftRight() {
synchronized (left) {
System.out.println(Thread.currentThread().getName()+" 拿到了left锁");
try {
//为了让另一个线程有执行机会,拿到第一个锁需要休眠一下
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (right) {
System.out.println(Thread.currentThread().getName()+" 拿到了right锁");
doSomething();
}
}
}
public void rightLeft() {
synchronized (right) {
System.out.println(Thread.currentThread().getName()+" 拿到了right锁");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (left) {
System.out.println(Thread.currentThread().getName()+" 拿到了left锁");
doSomethingElse();
}
}
}
void doSomething() {
}
void doSomethingElse() {
}
public static void main(String[] args) {
LeftRightDeadlock leftRight = new LeftRightDeadlock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
leftRight.leftRight();
}
},"bam");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
leftRight.rightLeft();
}
},"boo");
t2.start();
}
}
运行结果:
此处是bam先执行,如下图所示:
在该案例中发生死锁的原因是:两个线程试图以不同的顺序来获得相同的锁,如果按照相同的顺序来请求锁,那么就不会出现循环的加锁依赖,也就不会产生死锁。
(2)动态的锁顺序死锁
①动态的锁顺序死锁
public class DynamicOrderDeadlock {
// Warning: deadlock-prone!
public static void transferMoney(Account fromAccount, Account toAccount, DollarAmount amount)
throws InsufficientFundsException {
synchronized (fromAccount) {
System.out.println(Thread.currentThread().getName()+"拿到了"+fromAccount.getAcctNo());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (toAccount) {
System.out.println(Thread.currentThread().getName()+"拿到了"+toAccount.getAcctNo()+"SUCCESS");
if (fromAccount.getBalance().compareTo(amount) < 0)
throw new InsufficientFundsException();
else {
fromAccount.debit(amount);
toAccount.credit(amount);
}
}
}
}
static class DollarAmount implements Comparable<DollarAmount> {
// Needs implementation
int amount;
public DollarAmount(int amount) {
this.amount = amount;
}
public DollarAmount add(DollarAmount d) {
amount += d.amount;
return this;
}
public DollarAmount subtract(DollarAmount d) {
amount -= d.amount;
return this;
}
public int compareTo(DollarAmount dollarAmount) {
return this.amount - dollarAmount.amount;
}
}
static class Account {
private DollarAmount balance;
private final int acctNo;
private static final AtomicInteger sequence = new AtomicInteger();
public Account() {
acctNo = sequence.incrementAndGet();
}
void debit(DollarAmount d) {
balance = balance.subtract(d);
}
void credit(DollarAmount d) {
balance = balance.add(d);
}
DollarAmount getBalance() {
return balance;
}
int getAcctNo() {
return acctNo;
}
}
static class InsufficientFundsException extends Exception {
}
public static void main(String[] args) throws InsufficientFundsException {
DollarAmount d1 = new DollarAmount(10);
DollarAmount d2 = new DollarAmount(10);
DollarAmount paid = new DollarAmount(5);
Account a1 = new Account();
a1.balance = d1;
Account a2 = new Account();
a2.balance = d2;
new Thread(new Runnable() {
@Override
public void run() {
try {
transferMoney(a1, a2, paid);
} catch (InsufficientFundsException e) {
e.printStackTrace();
}
}
},"a1Toa2").start();
new Thread(new Runnable() {
@Override
public void run() {
try {
transferMoney(a2, a1, paid);
} catch (InsufficientFundsException e) {
e.printStackTrace();
}
}
},"a2Toa1").start();
System.out.println("a1:" + a1.getBalance().amount + "====a2:" + a2.getBalance().amount);
}
}
运行结果:
改进:通过锁顺序来避免死锁
public static void transferMoney(Account fromAccount, Account toAccount, DollarAmount amount)
throws InsufficientFundsException {
class Helper{
public void transfer() throws InsufficientFundsException {
if(fromAccount.getBalance().compareTo(amount)< 0)
throw new InsufficientFundsException();
else {
fromAccount.debit(amount);
toAccount.credit(amount);
}
}
}
int fromHash = System.identityHashCode(fromAccount);
int toHash = System.identityHashCode(toAccount);
if(fromHash<toHash) {
synchronized (fromAccount) {
synchronized (toAccount) {
new Helper().transfer();
}
}
}else if(fromHash>toHash) {
synchronized (toAccount) {
synchronized (fromAccount) {
new Helper().transfer();
}
}
}else {
synchronized (tieLock) {
synchronized (fromAccount) {
synchronized (toAccount) {
new Helper().transfer();
}
}
}
}
}
②在典型条件下会发生死锁的循环
public class DemonstrateDeadlock {
private static final int NUM_THREADS = 20;
private static final int NUM_ACCOUNTS = 5;
private static final int NUM_ITERATIONS = 1000000;
public static void main(String[] args) {
final Random rnd = new Random();
final Account[] accounts = new Account[NUM_ACCOUNTS];
for (int i = 0; i < accounts.length; i++) {
accounts[i] = new Account();
DollarAmount d1 = new DollarAmount(10);
accounts[i].balance =d1;
}
class TransferThread extends Thread {
public void run() {
for (int i = 0; i < NUM_ITERATIONS; i++) {
int fromAcct = rnd.nextInt(NUM_ACCOUNTS);
int toAcct = rnd.nextInt(NUM_ACCOUNTS);
DollarAmount amount = new DollarAmount(rnd.nextInt(1000));
try {
DynamicOrderDeadlock.transferMoney(accounts[fromAcct], accounts[toAcct], amount);
} catch (DynamicOrderDeadlock.InsufficientFundsException ignored) {
}
}
}
}
for (int i = 0; i < NUM_THREADS; i++)
new TransferThread().start();
}
}
(3)在协作对象之间发生的死锁
例子:
public class CooperatingDeadlock {
class Taxi {
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) {//线程1:获取Taxi上的锁
this.location = location;
if (location.equals(destination))
dispatcher.notifyAvailable(this);//线程1:获取Dispatcher的锁
}
public synchronized Point getDestination() {
return destination;
}
public synchronized void setDestination(Point destination) {
this.destination = destination;
}
}
class Dispatcher {
private final Set<Taxi> taxis;
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 synchronized Image getImage() {//线程2:获取Dispatcher的锁
Image image = new Image();
for (Taxi t : taxis)
image.drawMarker(t.getLocation());//线程2:获取每一个Taxi的锁
return image;
}
}
class Image {
public void drawMarker(Point p) {
System.out.println();
}
}
}
(4)开放调用
class Taxi {
private Point location, destination;
private final Dispatcher dispatcher;
...
public synchronized Point getLocation() {
return location;
}
public void setLocation(Point location) {
boolean reachedDestination;
synchronized (this) {
this.location = location;
reachedDestination = location.equals(destination);
}
if (reachedDestination)
dispatcher.notifyAvailable(this);
}
...
}
class Dispatcher {
private final Set<Taxi> taxis;
private final Set<Taxi> availableTaxis;
...
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;
}
}
(5)资源死锁
①
②
2、如何避免死锁
(1)使用支持定时的锁
(2)使用Thread Dump来分析死锁(JDK自带命令工具jps、jstack等)
3、活锁