10.1 死锁
哲学家问题
有环
A 等 B,B 等 A
数据库往往可以检测和解决死锁 //TODO
JVM 不行, 一旦死锁只有停止重启
下面分别介绍了几种典型的死锁情况:
10.1.1 Lock ordering Deadlocks
下面是一个经典的锁顺序死锁: 两个线程用不同的顺序来获得相同的锁, 如果按照锁的请求顺序来请求锁, 就不会发生这种循环依赖的情况
- 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 rightLeft() {
- synchronized (right) {
- synchronized (left) {
- doSomethingElse();
- }
- }
- }
- void doSomething() {
- }
- void doSomethingElse() {
- }
- }
- 10.1.1 Dynamic Lock Order Deadlocks
下面的转账例子, 如果一个线程 X 向 Y 转, 而另外一个线程 Y 向 X 也转, 那么就会发生死锁
- 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 {
- }
- }
解决办法还是顺序话锁, 考虑针对两种情况取 hashcode 然后判断 if-else 里面决定锁顺序
- class Helper {
- public void transfer() throws InsufficientFundsException {
- if (fromAcct.getBalance().compareTo(amount) < 0)
- throw new InsufficientFundsException();
- else {
- fromAcct.debit(amount);
- toAcct.credit(amount);
- }
- }
- }
- 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();
- }
- }
- }
- }
10.1.3 在协作对象之间发生死锁 Deadlocks Between Cooperating Objects
下面的例子 setLocation 和 getImage 都会获取两把锁, 会存在两个线程按照不同的顺序获取锁的情况
- public class CooperatingDeadlock {
- // Warning: deadlock-prone!
- class Taxi {
- @GuardedBy("this") 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) {
- this.location = location;
- if (location.equals(destination))
- dispatcher.notifyAvailable(this);
- }
- public synchronized Point getDestination() {
- return destination;
- }
- public synchronized void setDestination(Point destination) {
- this.destination = destination;
- }
- }
- class Dispatcher {
- @GuardedBy("this") private final Set<Taxi> taxis;
- @GuardedBy("this") 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() {
- Image image = new Image();
- for (Taxi t : taxis)
- image.drawMarker(t.getLocation());
- return image;
- }
- }
- class Image {
- public void drawMarker(Point p) {
- }
- }
- }
10.1.4 开放调用
减小锁的力度, 锁不嵌套
- class CooperatingNoDeadlock {
- @ThreadSafe
- class Taxi {
- @GuardedBy("this") 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) {
- boolean reachedDestination;
- synchronized (this) {
- this.location = location;
- reachedDestination = location.equals(destination);
- }
- if (reachedDestination)
- dispatcher.notifyAvailable(this);
- }
- public synchronized Point getDestination() {
- return destination;
- }
- public synchronized void setDestination(Point destination) {
- this.destination = destination;
- }
- }
- @ThreadSafe
- class Dispatcher {
- @GuardedBy("this") private final Set<Taxi> taxis;
- @GuardedBy("this") 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 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;
- }
- }
- class Image {
- public void drawMarker(Point p) {
- }
- }
- }
1.0.15 资源死锁
数据库连接池, A 持有数据库 D1 连接, 等待与 D2 连接, B 持有 D2 的连接, 等待与 D1 连接
线程饥饿死锁, 如 8.1.1 小节的例子
10.2 死锁的避免与诊断
10.2.1 支持定时的锁
tryLock
10.2.2 kill -3 发信号给 JVM dump 线程
10.3 其他活跃性危险
10.3.1 饥饿
10.3.3 活锁 Livelock
他不会阻塞线程, 但是也不能继续执行, 因为线程在不断的重复执行相同的操作, 而且总会失败
例如处理事务消, 回滚后再次重新把任务放在队头
又例如发送数据包, 都选择 1s 后重试, 那么总会冲突, 所以可以考虑一个随机数时间间隔
来源: http://www.jianshu.com/p/1f697e0d1566