java 鎖機制(synchronized 與 Lock)

     在java中,解決同步問題,不少時候都會使用到synchronized和Lock,這二者都是在多線程併發時候常使用的鎖機制。java

     synchronized是java中的一個關鍵字,也就是說是java內置的一個特性。當一個線程訪問一個被synchronized修飾的代碼塊,會自動獲取對應的一個鎖,並在執行該代碼塊時,其餘線程想訪問這個代碼塊,會一直處於等待狀態,自有等該線程釋放鎖後,其餘線程進行資源競爭,競爭獲取到鎖的線程才能訪問該代碼塊。多線程

  線程釋放synchronized修飾的代碼塊鎖的方式有兩種:併發

  1. 該線程執行完對應代碼塊,自動釋放鎖。
  2. 在執行該代碼塊是發生了異常,JVM會自動釋放鎖。

  採用synchronized關鍵字來實現同步,會致使若是存在多個線程想執行該代碼塊,而當前獲取到鎖的線程又沒有釋放鎖,可想而知,其餘線程只有一隻等待,這將嚴重印象執行效率。Lock鎖機制的出現就是爲了解決該現象。Lock是一個java接口,經過這個接口能夠實現同步,使用Lock時,用戶必須手動進行鎖的釋放,不然容易出現死鎖。ide

       

  ReentranLock是Lock的惟一實現類。下面簡單介紹一下ReentranLock與synchronized的區別:測試

  • Synchronized是一個同步鎖。當一個線程A訪問synchronized修飾的代碼塊時,線程A就會獲取該代碼塊的鎖,若是這時存在其餘線程範圍該代碼塊時,將會阻塞,可是不影響這些線程訪問其餘非同步代碼塊。
  • ReentranLock是可重入鎖。由構造方法可知,該鎖支持兩種鎖模式,公平鎖和非公平鎖。默認是非公平的。

  公平鎖:當線程A獲取訪問該對象,獲取到鎖後,此時內部存在一個計數器num+1,其餘線程想訪問該對象,就會進行排隊等待(等待隊列最前一個線程處於待喚醒狀態),直到線程A釋放鎖(num = 0),此時會喚醒處於待喚醒狀態的線程進行獲取鎖的操做,一直循環。若是線程A再次嘗試獲取該對象鎖是,會檢查該對象鎖釋放已經被佔用,若是被佔用,會作一次是否爲當前線程佔用鎖的判斷,若是是內部計數器num+1,而且不須要進入等待隊列,而是直接回去當前鎖。spa

  非公平鎖:當線程A在釋放鎖後,等待對象的線程會進行資源競爭,競爭成功的線程將獲取該鎖,其餘線程繼續睡眠。操作系統

  公平鎖是嚴格的以FIFO的方式進行鎖的競爭,可是非公平鎖是無序的鎖競爭,剛釋放鎖的線程很大程度上能比較快的獲取到鎖,隊列中的線程只能等待,因此非公平鎖可能會有「飢餓」的問題。可是重複的鎖獲取能減少線程之間的切換,而公平鎖則是嚴格的線程切換,這樣對操做系統的影響是比較大的,因此非公平鎖的吞吐量是大於公平鎖的,這也是爲何JDK將非公平鎖做爲默認的實現。線程

  下面是接口Lock的方法:code

  

  附上對接口Lock方法的測試,有什麼問題歡迎各位大佬留言評論。對象

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
    // ReentrantLock爲Lock的惟一實現類
    private Lock lock = new ReentrantLock();

    /**
     * 測試使用lock 的 lock()方法 :若是鎖已經被其餘線程獲取,則等待
     * @param thread
     */
    public void testLock(Thread thread){
        try {
            // 1.獲取鎖
            lock.lock();
            System.out.println("線程 " + thread.getName() + " 獲取了鎖!");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println("線程 " + thread.getName() + " 釋放了鎖!");
            // 必須在 finally 中釋放鎖,防止死鎖
            lock.unlock();
        }
    }

    /**
     * 測試使用lock 的 lock()方法 :經過這個方法去獲取鎖時,若是線程正在等待獲取鎖,則這個線程可以響應中斷,即中斷線程的等待狀態。
     * @param thread
     */
    public void testLockInterruptibly(Thread thread){
        try {
            // 1.獲取鎖
            lock.lockInterruptibly();
            System.out.println("線程 " + thread.getName() + " 獲取了鎖!");
            Thread.sleep(3000);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println("線程 " + thread.getName() + " 釋放了鎖!");
            // 必須在 finally 中釋放鎖,防止死鎖
            lock.unlock();
        }
    }

    /**
     * 測試使用lock 的 tryLock()方法 :若是獲取成功,則返回true,若是獲取失敗(即鎖已被其餘線程獲取),則返回false
     * @param thread
     */
    public void testTryLock(Thread thread){
        if(lock.tryLock()){// 若是獲取到了鎖
            try {
                System.out.println("線程 " + thread.getName() + " 獲取了鎖!");
                Thread.sleep(3000);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                System.out.println("線程 " + thread.getName() + " 釋放了鎖!");
                // 必須在 finally 中釋放鎖,防止死鎖
                lock.unlock();
            }
        }else {
            // 沒有獲取到鎖
            System.out.println("線程 " + thread.getName() + " 沒有獲取到鎖!");
        }
    }

    /**
     * 測試使用lock 的 tryLock(long time, TimeUnit unit)方法 :和tryLock()方法是相似的,只不過區別在於這個方法在拿不到鎖時會等待必定的時間,
     * 在時間期限以內若是還拿不到鎖,就返回false。若是若是一開始拿到鎖或者在等待期間內拿到了鎖,則返回true。
     * @param thread
     */
    public void testTryLock_time_unit(Thread thread){
        try {
            if(lock.tryLock(1000, TimeUnit.MILLISECONDS)){// 若是獲取到了鎖
                try {
                    System.out.println("線程 " + thread.getName() + " 獲取了鎖!");
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    System.out.println("線程 " + thread.getName() + " 釋放了鎖!");
                    // 必須在 finally 中釋放鎖,防止死鎖
                    lock.unlock();
                }
            }else {
                // 沒有獲取到鎖
                System.out.println("線程 " + thread.getName() + " 沒有獲取到鎖!");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args){
        TestLock testLock = new TestLock();
        Thread a = new Thread("A") {
            @Override
            public void run() {
                /*// 測試 lock()
                testLock.testLock(Thread.currentThread());*/
                /*// 測試 lockInterruptibly()
                testLock.testLockInterruptibly(Thread.currentThread());*/
                /*// 測試 tryLock()
                testLock.testTryLock(Thread.currentThread());*/
                /*// 測試 tryLock(long time, TimeUnit unit)
                testLock.testTryLock_time_unit(Thread.currentThread());*/
                testLock.testTryLock_time_unit(Thread.currentThread());
            }
        };
        Thread b = new Thread("B") {
            @Override
            public void run() {
                testLock.testTryLock(Thread.currentThread());
            }
        };
        a.start();
        b.start();
    }
}
相關文章
相關標籤/搜索