企业网站功能需求文档,网站开发课程设计建议,小企业网站建设包含哪些,大连百度推广优化死锁
在Java中使用多线程#xff0c;就会有可能导致死锁问题。死锁会让程序一直卡住#xff0c;程序不再往下执行。
我们只能通过中止并重启的方式来让程序重新执行。 这是我们非常不愿意看到的一种现象#xff0c;我们要尽可能避免死锁的情况发生#xff01;
死锁的原因…死锁
在Java中使用多线程就会有可能导致死锁问题。死锁会让程序一直卡住程序不再往下执行。
我们只能通过中止并重启的方式来让程序重新执行。 这是我们非常不愿意看到的一种现象我们要尽可能避免死锁的情况发生
死锁的原因: 当前线程拥有其他线程需要的资源当前线程等待其他线程已拥有的资源都不放弃自己拥有的资源 锁顺序死锁 线程1调用leftRight()方法得到left锁 同时线程2调用rightLeft()方法得到right锁线程1和线程2都继续执行此时线程1需要right锁才能继续往下执行。此时线程2需要left锁才能继续往下执行。 线程1的left锁并没有释放线程2的right锁也没有释放。 所以他们都只能等待而这种等待是无期限的–永久等待–死锁 class Demo implements Runnable{private final Object left new Object(); private final Object right new Object();Overridepublic void run() {leftRight();rightLeft();}public void leftRight() {synchronized (left) {System.out.println(线程 Thread.currentThread().getName() 拿到left还需要right);synchronized (right) {System.out.println(线程 Thread.currentThread().getName() 拿到left和right);}}}public void rightLeft() {synchronized (right) {System.out.println(线程 Thread.currentThread().getName() 拿到right还需要left);synchronized (left) {System.out.println(线程 Thread.currentThread().getName() 拿到right和left);}}}
}动态锁顺序死锁 代码释义获取源账户与目标账户判断余额充足后进行加减操作。 死锁现象线程1源账户a目标账户b。线程2源账户b目标账户a。 public class ThreadTest {public static void main(String[] args) {Test test new Test();new Thread(test, 1).start();new Thread(test, 2).start();new Thread(test, 3).start();new Thread(test, 4).start();}
}class Test implements Runnable {Account a new Account(A, 1000);Account b new Account(B, 1000);Overridepublic void run() {transferMoney(a, b, 100);transferMoney(b, a, 100);}public void transferMoney(Account fromAccount, Account toAccount, double money) {synchronized (fromAccount) {System.out.println(线程 Thread.currentThread().getName() 获得账户 fromAccount.getName());synchronized (toAccount) {System.out.println(线程 Thread.currentThread().getName() 获得账户 toAccount.getName());if (fromAccount.getMoney() money) {System.out.println(余额不足);} else {fromAccount.setMoney(fromAccount.getMoney() - money);toAccount.setMoney(toAccount.getMoney() money);System.out.println(转账后 fromAccount.getName() 的余额 fromAccount.getMoney());System.out.println(转账后 toAccount.getName() 的余额 toAccount.getMoney());}}}}
}
Data
class Account {public Account(String name, double money) {this.name name;this.money money;}private String name;private double money;
}协作对象之间发生死锁
getImage()和setLocation(Point location)都需要获取两个锁且在操作途中是没有释放锁的。
这就是隐式获取两个锁(对象之间协作)很容易造成死锁
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;}// setLocation 需要Taxi内置锁public synchronized void setLocation(Point location) {this.location location;if (location.equals(destination))// 调用notifyAvailable()需要Dispatcher内置锁dispatcher.notifyAvailable(this);}public synchronized Point getDestination() {return destination;}public synchronized void setDestination(Point destination) {this.destination destination;}}class Dispatcher {private final SetTaxi taxis;private final SetTaxi availableTaxis;public Dispatcher() {taxis new HashSetTaxi();availableTaxis new HashSetTaxi();}public synchronized void notifyAvailable(Taxi taxi) {availableTaxis.add(taxi);}// 调用getImage()需要Dispatcher内置锁public synchronized Image getImage() {Image image new Image();for (Taxi t : availableTaxis)// 调用getLocation()需要Taxi内置锁image.drawMarker(t.getLocation());return image;}}class Image {public void drawMarker(Point p) {}}
}避免死锁的方法
避免死锁可以概括成三种方法
固定加锁的顺序(针对锁顺序死锁)开放调用(针对对象之间协作造成的死锁)使用定时锁–tryLock()如果等待获取锁时间超时则抛出异常而不是一直等待
固定锁顺序避免死锁
得到对应的hash值来固定加锁的顺序这样不会出现死锁。
public class OrderLock {private static final Object tieLock new Object();public void transferMoney(final Account fromAccount, final Account toAccount, final 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();}}}}}
}开放调用避免死锁
在协作对象之间发生死锁的例子中主要是因为在调用某个方法时就需要持有锁并且在方法内部也调用了其他带锁的方法。
如果在调用某个方法时不需要持有锁那么这种调用被称为开放调用。同步代码块最好仅被用于保护那些涉及共享状态的操作。
class CooperatingLock {ThreadSafeclass 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 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;}}ThreadSafeclass Dispatcher {GuardedBy(this)private final SetTaxi taxis;GuardedBy(this)private final SetTaxi availableTaxis;public Dispatcher() {taxis new HashSetTaxi();availableTaxis new HashSetTaxi();}public synchronized void notifyAvailable(Taxi taxi) {availableTaxis.add(taxi);}public Image getImage() {SetTaxi copy;synchronized (this) {copy new HashSetTaxi(availableTaxis);}Image image new Image();for (Taxi t : copy)image.drawMarker(t.getLocation());return image;}}class Image {public void drawMarker(Point p) {}}
}
使用定时锁
使用显式Lock锁在获取锁时使用tryLock()方法。当等待超时的时候tryLock()不会一直等待而是返回错误信息。
使用tryLock()能够有效避免死锁问题。tryLock()方法是有返回值的它表示用来尝试获取锁如果获取成功则返回true如果获取失败即锁已被其他线程获取则返回false这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。
public class tryLock {public static void main(String[] args) {System.out.println(开始);final Lock lock new ReentrantLock();new Thread() {Overridepublic void run() {String tName Thread.currentThread().getName();if (lock.tryLock()) {System.out.println(tName 获取到锁);} else {System.out.println(tName 获取不到锁);return;}try {for (int i 0; i 5; i) {System.out.println(tName : i);}Thread.sleep(5000);} catch (Exception e) {System.out.println(tName 出错了);} finally {System.out.println(tName 释放锁);lock.unlock();}}}.start();new Thread() {Overridepublic void run() {String tName Thread.currentThread().getName();if (lock.tryLock()) {System.out.println(tName 获取到锁);} else {System.out.println(tName 获取不到锁);return;}try {for (int i 0; i 5; i) {System.out.println(tName : i);}} catch (Exception e) {System.out.println(tName 出错了);} finally {System.out.println(tName 释放锁);lock.unlock();}}}.start();System.out.println(结束);}
}总结
发生死锁的原因主要由于 线程之间交错执行 解决以固定的顺序加锁 执行某方法时就需要持有锁且不释放 解决缩减同步代码块范围最好仅操作共享变量时才加锁 永久等待 解决使用tryLock()定时锁超时则返回错误信息