本文分析一段旨在實(shí)現(xiàn)三個(gè)線程交替打印自身ID的代碼,并解釋其中出現(xiàn)的IllegalMonitorStateException異常。該代碼嘗試使用共享字符串變量current_thread控制線程執(zhí)行順序,但由于不當(dāng)使用wait()和notifyAll()方法導(dǎo)致錯(cuò)誤。
以下為問(wèn)題代碼片段:
package 并發(fā)編程.work2; public class Test { private static volatile String CURRENT_THREAD = "A"; public static void main(String[] args) { Thread t1 = new Thread(new PrintThreadName(), "A"); Thread t2 = new Thread(new PrintThreadName(), "B"); Thread t3 = new Thread(new PrintThreadName(), "C"); t1.start(); t2.start(); t3.start(); } static class PrintThreadName implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { synchronized (CURRENT_THREAD) { while (!Thread.currentThread().getName().equals(CURRENT_THREAD)) { try { CURRENT_THREAD.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + ":" + i); CURRENT_THREAD = CURRENT_THREAD.equals("A") ? "B" : (CURRENT_THREAD.equals("B") ? "C" : "A"); CURRENT_THREAD.notifyAll(); } } } } }
異常原因分析:
代碼的核心問(wèn)題在于current_thread變量的用法。代碼試圖將current_thread用作鎖對(duì)象,并在持有鎖的同時(shí)修改其值。當(dāng)一個(gè)線程執(zhí)行current_thread.wait()進(jìn)入等待狀態(tài)后,另一個(gè)線程修改了current_thread的值。等待線程被喚醒后,它試圖在新的current_thread對(duì)象上釋放鎖,而這個(gè)對(duì)象并非它之前獲取鎖的對(duì)象,因此拋出IllegalMonitorStateException異常。wait()方法要求在持有鎖對(duì)象的線程上調(diào)用,而修改current_thread后,鎖對(duì)象已改變。
解決方案:
避免在釋放鎖之前修改鎖對(duì)象本身。應(yīng)使用一個(gè)獨(dú)立的、不會(huì)被修改的對(duì)象作為鎖,例如一個(gè)Object實(shí)例。這樣確保所有線程都在同一個(gè)鎖對(duì)象上進(jìn)行同步操作,避免IllegalMonitorStateException異常。 修改后的代碼如下:
package 并發(fā)編程.work2; public class Test { private static final Object LOCK = new Object(); private static volatile String CURRENT_THREAD = "A"; public static void main(String[] args) { // ... (rest of the main method remains the same) } static class PrintThreadName implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { synchronized (LOCK) { // 使用獨(dú)立的鎖對(duì)象LOCK while (!Thread.currentThread().getName().equals(CURRENT_THREAD)) { try { LOCK.wait(); // 在LOCK對(duì)象上等待 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + ":" + i); CURRENT_THREAD = CURRENT_THREAD.equals("A") ? "B" : (CURRENT_THREAD.equals("B") ? "C" : "A"); LOCK.notifyAll(); // 在LOCK對(duì)象上喚醒 } } } } }
通過(guò)使用獨(dú)立的鎖對(duì)象LOCK,解決了IllegalMonitorStateException異常,并保證了線程的正確同步。