簡單順序死鎖
package com.txr.TransferMoneyDemo;
/**
* Created by txr on 2017/7/28.
*/
public class LeftRightDeadlock {
private final Object left =new Object();
private final Object right=new Object();
public void leftRight(){
synchronized (left){
synchronized (right){
dosomething();
}
}
}
public void rigthLeft(){
synchronized (right){
synchronized (left){
dosomethingElse();
}
}
}
}
顯然這樣會出現死鎖,比如A-->B B--->A,這個時候第一個線程獲得了鎖A等待獲取鎖B,第二個線程線程獲取了鎖B等待獲取鎖A,這樣就形成了死鎖
動態的鎖順序死鎖
package com.txr.TransferMoneyDemo;
import javax.naming.InsufficientResourcesException;
/**
* Created by zj-db0236 on 2017/7/28.
*/
public class LeftRightDeadlock {
public void transferMoney(Account fromAcct,Account toAcct,DollarAmout amout) throws InsufficientResourcesException {
synchronized (fromAcct){
synchronized (toAcct){
if(fromAcct.getBalance().compareTo(amout)<0)
throw new InsufficientResourcesException();
else{
fromAcct.debit(amout);
toAcct.credit(amout);
}
}
}
}
}
這種死鎖也很容易理解,比如 A--->B 另一個線程 B--->A
通過鎖順序來避免死鎖
private static final Object tieLock =new Object();
/**
* 以順序鎖的形式來轉錢,System.identityHashCode是個本地方法,
* 是以引用地址來計算的,HashCode是以值來計算的
* @param fromAcct 轉賬人
* @param toAcct 被轉賬人
* @param amout 錢
* @throws InsufficientResourcesException
*/
public static void transferMoney(final Account fromAcct,
final Account toAcct, final DollarAmout amout) throws InsufficientResourcesException {
class Helper{
public void transfer() throws InsufficientResourcesException{
//轉賬人出賬
fromAcct.debit(amout);
//被轉賬人入賬
toAcct.credit(amout);
}
}
int fromHash =System.identityHashCode(fromAcct);
int toHash = System.identityHashCode(toAcct);
if(fromHash<toHash){
synchronized (fromAcct){
synchronized (toAcct){
new Helper().transfer();
}
}
}else if(fromHash>toHash) {
synchronized (toAcct) {
synchronized (fromAcct) {
new Helper().transfer();
}
}
}else{
synchronized (tieLock){
synchronized (fromAcct){
synchronized (toAcct){
new Helper().transfer();
}
}
}
}
}
以下來坑了,書上說如下代碼典型情況下會發生死鎖
public class IdentityHashCodeDemo {
private static final int NUM_THREADS=20;
private static final int NUM_ACCOUNTS=5;
private static final int NUM_ITERATIONS=100000;
/**
* 模擬十萬次轉賬居然會發生死鎖這是爲什麼?
* @param args
*/
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();
//模擬十萬次轉賬,不考慮賬戶爲負數的情況
class TransferThread extends Thread{
@Override
public void run() {
for(int i=0;i<NUM_ITERATIONS;i++){
int fromAcct=rnd.nextInt(NUM_ACCOUNTS);
int toAcct=rnd.nextInt(NUM_ACCOUNTS);
DollarAmout amout=new DollarAmout(rnd.nextInt(1000));
try {
//很納悶明明用了順序鎖爲什麼還會很快出現死鎖
transferMoney(accounts[fromAcct],accounts[toAcct],amout);
} catch (InsufficientResourcesException e) {
e.printStackTrace();
}
}
}
}
for (int i=0;i<NUM_THREADS;i++)
new TransferThread().start();
}
}
百思不得其解,最後沒辦法上網把源碼當下來了,查看源碼傻眼了,源碼如是寫着
package net.jcip.examples;
import java.util.*;
import net.jcip.examples.DynamicOrderDeadlock.Account;
import net.jcip.examples.DynamicOrderDeadlock.DollarAmount;
/**
* DemonstrateDeadlock
* <p/>
* Driver loop that induces deadlock under typical conditions
*
* @author Brian Goetz and Tim Peierls
*/
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();
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();
}
}
看到了什麼?擔心你們粗心我給勾出來了package net.jcip.examples;
import java.util.concurrent.atomic.*;
/**
* DynamicOrderDeadlock
* <p/>
* Dynamic lock-ordering deadlock
*
* @author Brian Goetz and Tim Peierls
*/
public class DynamicOrderDeadlock {
// Warning: deadlock-prone!
public static void transferMoney(Account fromAccount,
Account toAccount,
DollarAmount amount)
throws InsufficientFundsException {
synchronized (fromAccount) {
synchronized (toAccount) {
if (fromAccount.getBalance().compareTo(amount) < 0)
throw new InsufficientFundsException();
else {
fromAccount.debit(amount);
toAccount.credit(amount);
}
}
}
}
static class DollarAmount implements Comparable<DollarAmount> {
// Needs implementation
public DollarAmount(int amount) {
}
public DollarAmount add(DollarAmount d) {
return null;
}
public DollarAmount subtract(DollarAmount d) {
return null;
}
public int compareTo(DollarAmount dollarAmount) {
return 0;
}
}
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 {
}
}
是不是看完就很好理解爲什麼在大多數系統下會很快發生死鎖了?好坑好坑好坑,一聲不說就直接用了個錯誤的類,以上也說明在附有源碼的時候,看書一定要對照源碼看,這樣能避免很多迷惑性的問題,寫此博客也希望能幫到很多跟我一樣有困惑的人【我百度,谷歌了很久始終沒有找到有人寫過這個】