Synchronized
偏向鎖,輕量級鎖 ,重量級鎖
偏向鎖:對象頭存儲線程ID,可重入(根據線程ID判斷)
輕量級鎖:復制對象頭到Lock Record 記錄鎖信息,擁有鎖 復制的Lock Rrecord 指向對象頭,自旋獲取鎖
重量級鎖:moniter監控 ,阻塞
Synchronized不同使用方法區別:
可重入鎖
可重入鎖又名遞歸鎖,是指在同一個線程在外層方法獲取鎖的時候,在進入內層方法會自動獲取鎖。說的有點抽象,下面會有一個代碼的示例。
-
對於Java ReentrantLock而言, 他的名字就可以看出是一個可重入鎖,其名字是Re entrant Lock重新進入鎖。
-
對於Synchronized而言,也是一個可重入鎖。可重入鎖的一個好處是可一定程度避免死鎖。
synchronized void setA() throws Exception{ Thread.sleep(1000); setB(); } synchronized void setB() throws Exception{ Thread.sleep(1000); }
上面的代碼就是一個可重入鎖的一個特點,如果不是可重入鎖的話,setB可能不會被當前線程執行,可能造成死鎖。
Synchronized和ReentrantLock區別:
1.比Synchronized更靈活
2.lock()獲取鎖,unlock()釋放鎖,要手動在finally中調用unlock()釋放鎖。
3.Synchronized驚群效應
Synchronized中的wait和notify ,而Lock是借助於Condition類實現,更靈活,可以選擇性的進行線程通知,在調度線程上更加靈活。
Synchronized驚群效應,當有一個線程獲取鎖時候,其他線程進入WaitSet隊列,wait()之后的nofity()時線程在爭奪同一把鎖的時候是隨機的,誰搶到就給誰。 notifyAll() 會有一種驚群效應,一旦鎖被釋放了,所有的wait線程被喚醒。被通知的線程是有JVM 隨機選擇的 ,Synchronized就相當於整個Lock對象中只有一個單一的Condition對象,所有的線程都注冊在它一個對象的身上,線程開始notiifyAll()時,需要通知所有的WAITING線程,沒有選擇權,會出現相當大的效率問題。
4.Lock對象可以創建多個Condition(對象監視器)實例,線程對象可以注冊在指定的Condition中,從而可以選擇性的進行線程的通知,在調度線程上更加靈活。
Lock lock=new ReentrantLock(); Condition condition=lock.newCondition();
condition.await();
condition.signal();
condition.signalAll();
Condition中的signalAll
/** 從Condition中移動所有的等待線程到 擁有鎖隊列里 * Moves all threads from the wait queue for this condition to * the wait queue for the owning lock. * 是 queue隊列而不是waitSet 集合 * @throws IllegalMonitorStateException if {@link #isHeldExclusively} * returns {@code false} */ public final void signalAll() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node first = firstWaiter; if (first != null) doSignalAll(first); }
/** * Removes and transfers all nodes. * @param first (non-null) the first node on condition queue */ private void doSignalAll(Node first) { lastWaiter = firstWaiter = null; do { Node next = first.nextWaiter; first.nextWaiter = null; transferForSignal(first); first = next; } while (first != null); }
為什么會有偏向鎖,輕量級鎖,重量級鎖:
1.為什么要引入偏向鎖?
因為經過HotSpot的作者大量的研究發現,大多數時候是不存在鎖競爭的,常常是一個線程多次獲得同一個鎖,因此如果每次都要競爭鎖會增大很多沒有必要付出的代價,為了降低獲取鎖的代價,才引入的偏向鎖。
2.為什么要引入輕量級鎖?
輕量級鎖考慮的是競爭鎖對象的線程不多,而且線程持有鎖的時間也不長的情景。因為阻塞線程需要CPU從用戶態轉到內核態,代價較大,如果剛剛阻塞不久這個鎖就被釋放了,那這個代價就有點得不償失了,因此這個時候就干脆不阻塞這個線程,讓它自旋這等待鎖釋放。
3.輕量級鎖什么時候升級為重量級鎖?
自旋的時間太長也不行,因為自旋是要消耗CPU的,因此自旋的次數是有限制的,比如10次或者100次,如果自旋次數到了線程1還沒有釋放鎖,或者線程1還在執行,
線程2還在自旋等待,這時又有一個線程3過來競爭這個鎖對象,那么這個時候輕量級鎖就會膨脹為重量級鎖。重量級鎖把除了擁有鎖的線程都阻塞,防止CPU空轉。
重量級鎖線程會被掛起park,耗性能
Synchronized的鎖升級過程