Synchronized實現原理及和Lock的區別


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的鎖升級過程

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM