活鎖
任務沒有被阻塞,由於某些條件沒有滿足,導致一直重復嘗試—失敗—嘗試—失敗的過程。 處於活鎖的實體是在不斷的改變狀態,活鎖有可能自行解開。
死鎖是大家都拿不到資源都占用着對方的資源,而活鎖是拿到資源卻又相互釋放不執行。
解決活鎖的一個簡單辦法就是在下一次嘗試獲取資源之前,隨機休眠一小段時間。
看一下,我們之前的一個例子,如果最后不進行隨機休眠,就會產生活鎖,現象就是很長一段時間,兩個線程都在不斷嘗試獲取和釋放鎖。
package constxiong.concurrency.a023; import java.util.Random; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 測試 占有部分資源的線程進一步申請其他資源時,如果申請不到,主動釋放它占有的資源,破壞 "不可搶占" 條件 * @author ConstXiong * @date 2019-09-24 14:50:51 */ public class TestBreakLockOccupation { private static Random r = new Random(); private static Lock lock1 = new ReentrantLock(); private static Lock lock2 = new ReentrantLock(); public static void main(String[] args) { new Thread(() -> { //標識任務是否完成 boolean taskComplete = false; while (!taskComplete) { lock1.lock(); System.out.println("線程:" + Thread.currentThread().getName() + " 獲取鎖 lock1 成功"); try { //隨機休眠,幫助造成死鎖環境 try { Thread.sleep(r.nextInt(30)); } catch (Exception e) { e.printStackTrace(); } //線程 0 嘗試獲取 lock2 if (lock2.tryLock()) { System.out.println("線程:" + Thread.currentThread().getName() + " 獲取鎖 lock2 成功"); try { taskComplete = true; } finally { lock2.unlock(); } } else { System.out.println("線程:" + Thread.currentThread().getName() + " 獲取鎖 lock2 失敗"); } } finally { lock1.unlock(); } //隨機休眠,避免出現活鎖 try { Thread.sleep(r.nextInt(10)); } catch (Exception e) { e.printStackTrace(); } } }).start(); new Thread(() -> { //標識任務是否完成 boolean taskComplete = false; while (!taskComplete) { lock2.lock(); System.out.println("線程:" + Thread.currentThread().getName() + " 獲取鎖 lock2 成功"); try { //隨機休眠,幫助造成死鎖環境 try { Thread.sleep(r.nextInt(30)); } catch (Exception e) { e.printStackTrace(); } //線程2 嘗試獲取鎖 lock1 if (lock1.tryLock()) { System.out.println("線程:" + Thread.currentThread().getName() + " 獲取鎖 lock1 成功"); try { taskComplete = true; } finally { lock1.unlock(); } } else { System.out.println("線程:" + Thread.currentThread().getName() + " 獲取鎖 lock1 失敗"); } } finally { lock2.unlock(); } //隨機休眠,避免出現活鎖 try { Thread.sleep(r.nextInt(10)); } catch (Exception e) { e.printStackTrace(); } } }).start(); } }
飢餓
一個線程因為 CPU 時間全部被其他線程搶占而得不到 CPU 運行時間,導致線程無法執行。
產生飢餓的原因:
- 優先級線程吞噬所有的低優先級線程的 CPU 時間
- 其他線程總是能在它之前持續地對該同步塊進行訪問,線程被永久堵塞在一個等待進入同步塊
- 其他線程總是搶先被持續地獲得喚醒,線程一直在等待被喚醒
package constxiong.concurrency.a024; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * 測試線程飢餓 * @author ConstXiong */ public class TestThreadHungry { private static ExecutorService es = Executors.newSingleThreadExecutor(); public static void main(String[] args) throws InterruptedException, ExecutionException { Future<String> future1 = es.submit(new Callable<String>() { @Override public String call() throws Exception { System.out.println("提交任務1"); Future<String> future2 = es.submit(new Callable<String>() { @Override public String call() throws Exception { System.out.println("提交任務2"); return "任務 2 結果"; } }); return future2.get(); } }); System.out.println("獲取到" + future1.get()); } }
打印結果如下,線程池卡死。線程池只能容納 1 個任務,任務 1 提交任務 2,任務 2 永遠得不到執行。
提交任務1