您的位置:首页 > 编程语言

并发编程实战死锁读书笔记之吐槽

2017-07-28 16:27 337 查看


简单顺序死锁

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

A:transferMoney(mycount ,yourAccount,1000)

B:transferMoney(yourcount ,myAccount,2000)

和上面一样会发生死锁


通过锁顺序来避免死锁

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();
}
}
看到了什么?担心你们粗心我给勾出来了



简直就苦笑不得,书上压根就没有提到这个类,他就默默的用了也不说,然后我们再来看看DynamicOrderDeadlock类

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 {
}
}


是不是看完就很好理解为什么在大多数系统下会很快发生死锁了?好坑好坑好坑,一声不说就直接用了个错误的类,以上也说明在附有源码的时候,看书一定要对照源码看,这样能避免很多迷惑性的问题,写此博客也希望能帮到很多跟我一样有困惑的人【我百度,谷歌了很久始终没有找到有人写过这个】
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: