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、活鎖