Java多线程环境检测系统中是否存在死锁和死锁恢复代码示例
文章目錄
- ManagementFactory介紹
- 死鎖檢測與恢復介紹
- 代碼
- 公共資源類
- 導致死鎖的模型
- 模型實現類
- 模擬死鎖的程序類
ManagementFactory介紹
關于ManagementFactory:
ManagementFactory是一個可以獲取JVM線程、內存、編譯等信息的一個工廠類
死鎖檢測與恢復介紹
由于導致死鎖的線程的不可控性(比如第三方軟件啟動的線程),因此死鎖恢復的實際可操作性并不強:對死鎖進行的故障恢復嘗試可能是徒勞的(故障線程可無法響應中斷)且有害的(可能導致活鎖等問題)。 死鎖的自動恢復有賴于線程的中斷機制,其基本思想是:定義一個工作者線程DeadlockDetector專門用于死鎖檢測與恢復。該線程定期檢測系統中是否存在死鎖,若檢測到死鎖,則隨機選取一個死鎖線程并給其發送中斷。該中斷使得一個任意的死鎖線程(目標線程)被Java虛擬機喚醒,從而使其拋出InterruptedException異常。這使得目標線程不再等待它本來永遠也無法申請到的資源,從而破壞了死鎖產生的必要條件中的“占用并等待資源”中的“等待資源”部分。
目標線程則通過對InterruptedException進行處理的方式來響應中斷:目標線程捕獲InterruptedException異常后將其已經持有的資源(鎖)主動釋放掉,這相當于破壞了死鎖產生的必要條件中的“占用并等待資源”中的“占用資源”部分。接著,DeadlockDetector繼續檢測系統中是否仍然存在死鎖,若存在,則繼續選中一個任意的死鎖線程并給其發送中斷,直到系統中不再存在死鎖。
用 ManagementFactory檢測死鎖:
需要用到getThreadMXBean()方法,這個方法返回 Java 虛擬機的線程系統的托管 bean。
代碼
檢測死鎖的線程:
//工作者線程 public class DeadlockDetector extends Thread {static final ThreadMXBean tmb = ManagementFactory.getThreadMXBean();/*** 檢測周期(單位為毫秒)*/private final long monitorInterval; //ManagementFactory是一個可以獲取JVM線程、內存、編譯等信息的一個工廠類public DeadlockDetector(long monitorInterval) {super("DeadLockDetector");setDaemon(true);this.monitorInterval = monitorInterval;}public DeadlockDetector() {this(2000);}public static ThreadInfo[] findDeadlockedThreads() {long[] ids = tmb.findDeadlockedThreads();return null == tmb.findDeadlockedThreads() ?new ThreadInfo[0] : tmb.getThreadInfo(ids);}public static Thread findThreadById(long threadId) {for (Thread thread : Thread.getAllStackTraces().keySet()) {if (thread.getId() == threadId) {return thread;}}return null;}public static boolean interruptThread(long threadID) {Thread thread = findThreadById(threadID);if (null != thread) {thread.interrupt();return true;}return false;}@Overridepublic void run() {ThreadInfo[] threadInfoList;ThreadInfo ti;int i = 0;try {for (;;) {// 檢測系統中是否存在死鎖threadInfoList = DeadlockDetector.findDeadlockedThreads();if (threadInfoList.length > 0) {// 選取一個任意的死鎖線程ti = threadInfoList[i++ % threadInfoList.length];Debug.error("Deadlock detected,trying to recover"+ " by interrupting%n thread(%d,%s)%n",ti.getThreadId(),ti.getThreadName());// 給選中的死鎖線程發送中斷DeadlockDetector.interruptThread(ti.getThreadId());continue;} else {Debug.info("No deadlock found!");i = 0;}Thread.sleep(monitorInterval);}// for循環結束} catch (InterruptedException e) {// 什么也不做;}} }DeadlockDetector是通過java.lang.management.ThreadMXBean.findDeadlockedThreads()調用來實現死鎖檢測的。ThreadMXBean.findDeadlockedThreads()能夠返回一組死鎖線程的線程編號。ThreadMXBean類是JMX(Java Management Extension)API的一部分,因此其提供的功能也可以通過jconsole、jvisualvm手工調用。
注意:通過ReentrantLock.lock()申請顯式鎖的,因此它無法響應中斷,也就無法支持死鎖的自動恢復。
發生死鎖的統一抽象類:
/*** 對哲學家進行抽象** @author Viscent Huang*/ public abstract class AbstractPhilosopher extends Thread implements Philosopher {protected final int id;protected final Chopstick left;protected final Chopstick right;public AbstractPhilosopher(int id, Chopstick left, Chopstick right) {super("Philosopher-" + id);this.id = id;this.left = left;this.right = right;}@Overridepublic void run() {for (;;) {think();eat();}}/** @see io.github.viscent.mtia.ch7.diningphilosophers.Philosopher#eat()*/@Overridepublic abstract void eat();protected void doEat() {Debug.info("%s is eating...%n", this);Tools.randomPause(10);}/** @see io.github.viscent.mtia.ch7.diningphilosophers.Philosopher#think()*/@Overridepublic void think() {Debug.info("%s is thinking...%n", this);Tools.randomPause(10);}@Overridepublic String toString() {return "Philosopher-" + id;} }`
公共資源類
/*** 筷子** @author Viscent Huang*/ public class Chopstick {public final int id;private Status status = Status.PUT_DOWN;public static enum Status {PICKED_UP,PUT_DOWN}public Chopstick(int id) {super();this.id = id;}public void pickUp() {status = Status.PICKED_UP;}public void putDown() {status = Status.PUT_DOWN;}public Status getStatus() {return status;}@Overridepublic String toString() {return "chopstick-" + id;} }導致死鎖的模型
``javapublic class BuggyLckBasedPhilosopher extends AbstractPhilosopher {/*** 為確保每個Chopstick實例有且僅有一個顯式鎖(而不重復創建)與之對應,<br>* 這里的map必須采用static修飾!*/protected final static ConcurrentMap<Chopstick, ReentrantLock> LOCK_MAP;static {LOCK_MAP = new ConcurrentHashMap<Chopstick, ReentrantLock>();}public BuggyLckBasedPhilosopher(int id, Chopstick left, Chopstick right) {super(id, left, right);// 每個筷子對應一個(唯一)鎖實例LOCK_MAP.putIfAbsent(left, new ReentrantLock());LOCK_MAP.putIfAbsent(right, new ReentrantLock());}@Overridepublic void eat() {// 先后拿起左手邊和右手邊的筷子if (pickUpChopstick(left) && pickUpChopstick(right)) {// 同時拿起兩根筷子的時候才能夠吃飯try{doEat();} finally {// 放下筷子putDownChopsticks(right, left);}}}// @SuppressFBWarnings(value = "UL_UNRELEASED_LOCK", // justification = "筷子對應的鎖由應用自身保障總是能夠被釋放")protected boolean pickUpChopstick(Chopstick chopstick) {final ReentrantLock lock = LOCK_MAP.get(chopstick);try {lock.lock();Debug.info("%s is picking up %s on his %s...%n",this, chopstick, chopstick == left ? "left" : "right");chopstick.pickUp();}catch (Exception e) {// 不大可能走到這里e.printStackTrace();lock.unlock();return false;}return true;}private void putDownChopsticks(Chopstick chopstick1, Chopstick chopstick2) {try {putDownChopstick(chopstick1);} finally {putDownChopstick(chopstick2);}}protected void putDownChopstick(Chopstick chopstick) {final ReentrantLock lock = LOCK_MAP.get(chopstick);try {Debug.info("%s is putting down %s on his %s...%n",this, chopstick, chopstick == left ? "left" : "right");chopstick.putDown();} finally {lock.unlock();}} }模型實現類
public class RecoverablePhilosopher extends BuggyLckBasedPhilosopher {public RecoverablePhilosopher(int id, Chopstick left, Chopstick right) {super(id, left, right);}@Overrideprotected boolean pickUpChopstick(Chopstick chopstick) {final ReentrantLock lock = LOCK_MAP.get(chopstick);try {lock.lockInterruptibly();//這里,pickUpChopstick方法在捕獲到lock.lockInterruptibly()拋出的InterruptedException后,主動將當前線程已持有的鎖釋放掉(即放下當前哲學家已持有的筷子)。利用這個改造后的哲學家模型,我們就可以再現死鎖的自動恢復的效果 } catch (InterruptedException e) {// 使當前線程釋放其已持有的鎖Debug.info("%s detected interrupt.", Thread.currentThread().getName());Chopstick theOtherChopstick = chopstick == left ? right : left;theOtherChopstick.putDown();LOCK_MAP.get(theOtherChopstick).unlock();return false;}try {Debug.info("%s is picking up %s on his %s...%n",this, chopstick, chopstick == left ? "left" : "right");chopstick.pickUp();} catch (Exception e) {// 不大可能走到這里e.printStackTrace();lock.unlock();return false;}return true;} }模擬死鎖的程序類
public class DiningPhilosopherProblem {public static void main(String[] args) throws Exception {int numOfPhilosopers;numOfPhilosopers = args.length > 0 ? Integer.valueOf(args[0]) : 2;// 創建筷子Chopstick[] chopsticks = new Chopstick[numOfPhilosopers];for (int i = 0; i < numOfPhilosopers; i++) {chopsticks[i] = new Chopstick(i);}String philosopherImplClassName = System.getProperty("x.philo.impl");if (null == philosopherImplClassName) {philosopherImplClassName = "DeadlockingPhilosopher";}Debug.info("Using %s as implementation.", philosopherImplClassName);for (int i = 0; i < numOfPhilosopers; i++) {// 創建哲學家createPhilosopher(philosopherImplClassName, i, chopsticks);}}private static void createPhilosopher(String philosopherImplClassName,int id, Chopstick[] chopsticks) throws Exception {int numOfPhilosopers = chopsticks.length;@SuppressWarnings("unchecked")Class<Philosopher> philosopherClass = (Class<Philosopher>) Class.forName(DiningPhilosopherProblem.class.getPackage().getName() + "."+ philosopherImplClassName);Constructor<Philosopher> constructor = philosopherClass.getConstructor(int.class, Chopstick.class, Chopstick.class);Philosopher philosopher = constructor.newInstance(id, chopsticks[id],chopsticks[(id + 1)% numOfPhilosopers]);philosopher.start();} }結果:
死鎖的自動恢復有賴于死鎖的線程能夠響應中斷。以上面RecoverablePhilosopher為例,如果我們在代碼開發與維護過程中能夠意識到它是可能導致死鎖的,那么我們應該采取的措施是規避死鎖(防患未然)而不是使其支持死鎖的自動恢復(為亡羊補牢做準備);相反,如果我們未能事先意識到死鎖這個問題,那么這個類的相關方法可能根本無法響應中斷,或者能夠響應中斷但是其響應的結果卻未必是DeadlockDetector所期望的——釋放其已持有的資源。 其次,自動恢復嘗試可能導致新的問題。例如,如果RecoverablePhilosopher(對中斷的響應方式是僅僅保留中斷標記而并不釋放其已持有的資源,即RecoverablePhilosopher.pickUpChopstick方法對InterruptedException異常的處理邏輯僅僅是調用Thread.currentThread().interrupt()以保留中斷標記,那么嘗試對這樣的死鎖線程進行恢復非但不能達到預期效果,反而會造成相應線程一直在嘗試申請鎖而一直無法申請成功,即產生活鎖!
總結
以上是生活随笔為你收集整理的Java多线程环境检测系统中是否存在死锁和死锁恢复代码示例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: springboot使用Template
- 下一篇: 【剪枝算法】通过网络瘦身学习高效的卷积网