《Java并发编程实战》笔记4——避免活跃性危险

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值