Java並發之synchronized關鍵字和Lock接口


歡迎點贊閱讀,一同學習交流,有疑問請留言 。
GitHub上也有開源 JavaHouse,歡迎star

引用

當開發過程中,我們遇到並發問題。怎么解決?

一種解決方式,簡單粗暴:上鎖。將千軍萬馬都給攔下來,只允許一個人過獨木橋。書面意思就是將並行的程序變成串行的程序。現實的鎖有門鎖、掛鎖和抽屜鎖等等。在Java中,我們的鎖就是synchronized關鍵字和Lock接口。

synchronized關鍵字

synchronized也叫同步鎖,是Java里面的關鍵字。我們可以猜測到synchronized原理也JVM虛擬機有關聯。

synchronized鎖的是對象。對象里面有一個叫做監視鎖(monitor)的東西,監視鎖依賴操作系統的互斥鎖(Mutex Lock)。操作系統切換線程其實就是從用戶態編程核心態(cpu的兩種狀態)。這個代價有點高,所以synchronized這個重量級鎖后面也引進了偏向鎖和輕量級鎖。

加鎖(監視鎖monitor)過程分析():

  1. 當monitor的進入數為0,線程A進入
  2. monitor的進入數為1
  3. 線程B想進入該monitor就會被阻塞。

線程A可以重復進入該monitor,所以synchronized是可重入鎖,和Lock實現的鎖一樣。

  • 程序驗證
public class SynchronizedTest {
    private static  int i = 0;
    public static void main(String[] args) {
        test();
    }

    public static void test(){
        synchronized (SynchronizedTest.class){
            synchronized (SynchronizedTest.class){
                i++;
            }
        }
    }
}
  • 運行結果
    程序正常運行,沒有報錯

synchronized可以修飾方法以及代碼塊,代碼塊就是上面重入鎖的例子。

  • 修飾方法
public class SynchronizedTest {
    static int n = 100;
    final static CountDownLatch start = new CountDownLatch(n);
    private static  int i = 0;
    public static void main(String[] args) throws InterruptedException {
        for (int j = 0; j < n; j++) {
            Thread thread = new Thread(new addNoSynchronized());
            thread.start();
        }
        start.await();
        System.out.println(i);
    }

    public static class addSynchronized implements Runnable{
        @Override
        public void run() {
            addSynchronized();
        }
        public static synchronized void addSynchronized(){
            for (int j = 0; j < 1000; j++) {
                i++;
            }
            start.countDown();
        }
    }
}

  • 運行結果
100000

如果去掉 synchronized 關鍵字的話,運行結果大概率不是 100000,因為線程不安全問題。

Lock接口

一般我們使用 ReentrantLock 類作為重入鎖,實現Lock接口。

  • 使用方法
public class ReentranLockTest {
    private static int j;
    private static int n = 100;
    private static CountDownLatch latch = new CountDownLatch(n);
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            new Thread(new LockTest()).start();
        }
        latch.await();
        System.out.println("結果為:"+j);
    }

    public static class LockTest implements Runnable{
        static Lock lock = new ReentrantLock();
        @Override
        public void run() {
            lockTest();
            latch.countDown();
        }
        private void lockTest() {
            lock.lock();
            try {
                for (int i = 0; i < 1000; i++) {
                        j++;
                }
            }finally {
                lock.unlock();
            }
        }
    }
}
  • 運行結果
結果為:100000

這里我們鎖住的 j++ 這塊資源區(公共資源),lock 是 static 關鍵字修飾的,是類對象,思考一下如果不是類對象會怎么樣?那就是連環鎖了(看圖)。

連環鎖.png

每一個線程都對可以用鑰匙解開這把鎖,對於程序而言,加鎖操作就沒有意義了。因為我們需要的是一個鎖。

歡迎關注我的微信公眾號

公眾號.jpg


免責聲明!

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



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