import java.util.LinkedList; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; //條件:Condition 為Lock增長條件,當滿足條件時,作什麼事情,如枷鎖或解鎖。等待喚醒 public class TestContaniner<T> { private final LinkedList<T> list = new LinkedList<>(); private final int MAX = 10; private int count = 0; private Lock lock = new ReentrantLock(); private Condition product = lock.newCondition(); private Condition consumer = lock.newCondition(); public int getCount() { return count; } public void put(T t) { lock.lock(); try { while (list.size() == MAX) { System.out.println(Thread.currentThread().getName() + "等待中。。。"); // 進入等待隊列,釋放鎖標記 // 藉助條件,進入的等待隊列 product.await(); } System.out.println(Thread.currentThread().getName() + "put..."); list.add(t); count++; // 藉助條件,喚醒全部的消費者 consumer.signalAll(); } catch (InterruptedException e1) { e1.printStackTrace(); } finally { lock.unlock(); } } public T get() { T t = null; lock.lock(); try { while (list.size() == 0) { System.out.println(Thread.currentThread().getName() + "等待..."); consumer.await(); } System.out.println(Thread.currentThread().getName() + "get..."); t = list.removeFirst(); count--; } catch (InterruptedException e1) { e1.printStackTrace(); } finally { lock.unlock(); } return t; } public static void main(String[] args) { final TestContaniner<String> c=new TestContaniner<>(); for(int i=0;i<10;i++){ new Thread(new Runnable(){ @Override public void run(){ for(int j=0;j<5;j++){ System.out.println(c.get()); } } },"consumer"+i).start(); } try{ TimeUnit.SECONDS.sleep(2); }catch (InterruptedException e1) { e1.printStackTrace(); } for(int i=0;i<2;i++){ new Thread(new Runnable(){ @Override public void run(){ for(int j=0;j<25;j++){ c.put("container value"+j); } } },"producer"+i).start(); } } }
Java 虛擬機中的同步(Synchronization)基於進入和退出管程(Monitor)對象實現。同步方 法 並非由 monitor enter 和 monitor exit 指令來實現同步的,而是由方法調用指令讀取運 行時常量池中方法的 ACC_SYNCHRONIZED 標誌來隱式實現的java
對象頭:存儲對象的 hashCode、鎖信息或分代年齡或 GC 標誌,類型指針指向對象的類 元數據,JVM 經過這個指針肯定該對象是哪一個類的實例等信息。 實例變量:存放類的屬性數據信息,包括父類的屬性信息 填充數據:因爲虛擬機要求對象起始地址必須是 8 字節的整數倍。填充數據不是必須存 在的,僅僅是爲了字節對齊 當在對象上加鎖時,數據是記錄在對象頭中。當執行 synchronized 同步方法或同步代碼 塊時,會在對象頭中記錄鎖標記,鎖標記指向的是 monitor 對象(也稱爲管程或監視器鎖) 的起始地址。每一個對象都存在着一個 monitor 與之關聯,對象與其 monitor 之間的關係有 存在多種實現方式,如 monitor 能夠與對象一塊兒建立銷燬或當線程試圖獲取對象鎖時自動生 成,但當一個 monitor 被某個線程持有後,它便處於鎖定狀態。 在 Java 虛擬機(HotSpot)中,monitor 是由 ObjectMonitor 實現的。 ObjectMonitor 中有兩個隊列,_WaitSet 和 _EntryList,以及_Owner 標記。其中_WaitSet 是用於管理等待隊列(wait)線程的,_EntryList 是用於管理鎖池阻塞線程的,_Owner 標記用於 記錄當前執行線程。線程狀態圖以下: 多線程
當多線程併發訪問 同一個同步代碼 時,首先會進入_EntryList,當線程獲取鎖標記後, monitor 中的_Owner 記錄此線程,並在 monitor 中的計數器執行遞增計算(+1),表明鎖定, 其餘線程在_EntryList 中繼續阻塞。若執行線程調用 wait 方法,則 monitor 中的計數器執行 賦值爲 0 計算,並將_Owner 標記賦值爲 null,表明放棄鎖,執行線程進如_WaitSet 中阻塞。 若執行線程調用 notify/notifyAll 方法,_WaitSet 中的線程被喚醒,進入_EntryList 中阻塞,等 待獲取鎖標記。若執行線程的同步代碼執行結束,一樣會釋放鎖標記,monitor 中的_Owner 標記賦值爲 null,且計數器賦值爲 0 計算併發