最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java之synchronized(含與ReentrantLock的區(qū)別解讀)

 更新時間:2025年01月03日 10:08:13   作者:心流時間  
文章主要介紹了`synchronized`和`ReentrantLock`的區(qū)別,包括它們的實現(xiàn)原理、公平性、靈活性、可中斷性等方面,同時,文章詳細解釋了`synchronized`的使用方法,包括修飾實例方法、靜態(tài)方法和代碼塊的情況,以及如何分析代碼是否互斥和可重入性

1. synchronized與ReentrantLock的區(qū)別

區(qū)別點synchronizedReentrantLock
是什么?關鍵字,是 JVM 層面通過監(jiān)視器實現(xiàn)的類,基于 AQS 實現(xiàn)的
公平鎖與否?非公平鎖支持公平鎖和非公平鎖,默認非公平鎖
獲取當前線程是否上鎖可以(isHeldByCurrentThread())
條件變量支持條件變量(newCondition())
異常處理在 synchronized 塊中發(fā)生異常,鎖會自動釋放在 ReentrantLock 中沒有在 finally 塊中正確地調用 unlock() 方法,則可能會導致死鎖
靈活性1自動加鎖和釋放鎖手動加鎖和釋放鎖
靈活性2允許嘗試去獲取鎖而不阻塞(如 tryLock 方法),并且可以指定獲取鎖等待的時間(如 tryLock(long time, TimeUnit unit))。
可中斷性不可中斷,除非發(fā)生了異常允許線程中斷另一個持有鎖的線程,這樣持有鎖的線程可以選擇放棄鎖并響應中斷。1.tryLock(long timeout, TimeUnit unit);2.lockInterruptibly()和interrupt()配合使用
鎖的內容對象,鎖信息保存在對象頭中int類型的變量來標識鎖的狀態(tài):private volatile int state;
鎖升級過程無鎖->偏向鎖->輕量級鎖->重量級鎖
使用位置普通方法、靜態(tài)方法、代碼塊代碼塊(方法里的代碼,初始化塊都是代碼塊)

2. synchronized的作用

在Java中,使用synchronized關鍵字可以確保任何時刻只有一個線程可以執(zhí)行特定的方法或者代碼塊。這有助于防止數(shù)據(jù)競爭條件(race conditions)和其他由于線程間共享資源而產生的問題。

當一個方法或代碼塊被聲明為synchronized,它意味著在該方法或代碼塊執(zhí)行期間,其他試圖獲得相同鎖的線程將被阻塞,直到持有鎖的線程釋放該鎖。這個鎖通常是對象的一個監(jiān)視器(monitor),對于靜態(tài)方法來說是類的Class對象,對于實例方法則是擁有該方法的對象。

synchronized可以限制對共享資源的訪問,它鎖定的并不是臨界資源,而是某個對象,只有線程獲取到這個對象的鎖才能訪問臨界區(qū),進而訪問臨界區(qū)中的資源。

保證線程安全。

當多個線程去訪問同一個類(對象或方法)的時候,該類都能表現(xiàn)出正常的行為(與自己預想的結果一致),那我們就可以說這個類是線程安全的。

造成線程安全問題的主要誘因有兩點

  1. 存在共享數(shù)據(jù)(也稱臨界資源)
  2. 存在多條線程共同操作共享數(shù)據(jù)

當存在多個線程操作共享數(shù)據(jù)時,需要保證同一時刻有且只有一個線程在操作共享數(shù)據(jù),其他線程必須等到該線程處理完數(shù)據(jù)后再進行,這種方式有個高尚的名稱叫互斥鎖,即能達到互斥訪問目的的鎖,也就是說當一個共享數(shù)據(jù)被當前正在訪問的線程加上互斥鎖后,在同一個時刻,其他線程只能處于等待的狀態(tài),直到當前線程處理完畢釋放該鎖。

在 Java 中,關鍵字 synchronized可以保證在同一個時刻,只有一個線程可以執(zhí)行某個方法或者某個代碼塊(主要是對方法或者代碼塊中存在共享數(shù)據(jù)的操作),同時我們還應該注意到synchronized另外一個重要的作用,synchronized可保證一個線程的變化(主要是共享數(shù)據(jù)的變化)被其他線程所看到(保證可見性,完全可以替代volatile功能)。

3. synchronized的使用

下面三種本質上都是鎖對象

3.1 修飾實例方法

作用于當前實例,進入同步代碼前需要先獲取實例的鎖

  • 示例代碼:
public class SynchronizedDemo2 {
    int num = 0;

    public synchronized void add() {
//    public void add() {
        for (int i = 0; i < 10000; i++) {
            num++;
        }
    }
    public static class AddDemo extends Thread {
        private SynchronizedDemo2 synchronizedDemo2;

        public AddDemo(SynchronizedDemo2 synchronizedDemo2) {
            this.synchronizedDemo2 = synchronizedDemo2;
        }
        @Override
        public void run() {
            this.synchronizedDemo2.add();
        }
    }
    public static void main(String[] args) throws InterruptedException {
    	// 要想拿到臨界資源,就必須先獲得到這個對象的鎖。
        SynchronizedDemo2 synchronizedDemo2 = new SynchronizedDemo2();
        
        AddDemo addDemo1 = new AddDemo(synchronizedDemo2);
        AddDemo addDemo2 = new AddDemo(synchronizedDemo2);
        AddDemo addDemo3 = new AddDemo(synchronizedDemo2);

        addDemo1.start();
        addDemo2.start();
        addDemo3.start();

        // 阻塞主線程
        addDemo1.join();
        addDemo2.join();
        addDemo3.join();

        // 打印結果
        System.out.println(synchronizedDemo2.num);

    }
}
  • 打?。?/li>

期望結果:30000

無synchronized結果:23885

有synchronized結果:30000

synchronize作用于實例方法需要注意:

  • 實例方法上加synchronized,線程安全的前提是,多個線程操作的是同一個實例,如果多個線程作用于不同的實例,那么線程安全是無法保證的
  • 同一個實例的多個實例方法上有synchronized,這些方法都是互斥的,同一時間只允許一個線程操作同一個實例的其中的一個synchronized方法

3.2 修飾靜態(tài)方法

作用于類的Class對象,進入修飾的靜態(tài)方法前需要先獲取類的Class對象的鎖

鎖定靜態(tài)方法需要通過類.class,或者直接在靜態(tài)方法上加上關鍵字。但是,類.class不能使用this來代替。

注:在同一個類加載器中,class是單例的,這也就能保證synchronized能夠只讓一個線程訪問臨界資源。

  • 示例代碼:
public class SynchronizedDemo1 {
    static int num = 0;

	// 加上synchronized保證線程安全
	public static synchronized void add() {
    // public static void add() {
        for (int i = 0; i < 10000; i++) {
            num++;
        }
    }

    // 同上
    public static void add1() {
        synchronized (SynchronizedDemo1.class) {
            for (int i = 0; i < 10000; i++) {
                num++;
            }
        }
    }

    public static class AddDemo extends Thread {
        @Override
        public void run() {
            SynchronizedDemo1.add();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        AddDemo addDemo1 = new AddDemo();
        AddDemo addDemo2 = new AddDemo();
        AddDemo addDemo3 = new AddDemo();
        addDemo1.start();
        addDemo2.start();
        addDemo3.start();

        // 阻塞主線程
        addDemo1.join();
        addDemo2.join();
        addDemo3.join();

        // 打印結果
        System.out.println(SynchronizedDemo1.num);

    }
}
  • 打?。?/li>

期望結果:30000

無synchronized結果:14207

有synchronized結果:30000

3.3 修飾代碼塊

需要指定加鎖對象(記做lockobj),在進入同步代碼塊前需要先獲取lockobj的鎖

若是this,相當于修飾實例方法

  • 示例代碼:
public class SynchronizedDemo3 {

    private static Object lockobj = new Object();
    private static int num = 0;

    public static void add() {
        synchronized (lockobj) {
            for (int i = 0; i < 10000; i++) {
                num++;
            }
        }
    }

    public static class AddDemo extends Thread {
        @Override
        public void run() {
            SynchronizedDemo3.add();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        AddDemo addDemo1 = new AddDemo();
        AddDemo addDemo2 = new AddDemo();
        AddDemo addDemo3 = new AddDemo();
        addDemo1.start();
        addDemo2.start();
        addDemo3.start();

        // 阻塞主線程
        addDemo1.join();
        addDemo2.join();
        addDemo3.join();

        // 打印結果
        System.out.println(SynchronizedDemo3.num);

    }
}
  • 打印:

期望結果:30000

無synchronized結果:28278

有synchronized結果:> 示例代碼:

4. 分析代碼是否互斥

分析代碼是否互斥的方法,先找出synchronized作用的對象是誰,如果多個線程操作的方法中synchronized作用的鎖對象一樣,那么這些線程同時異步執(zhí)行這些方法就是互斥的。

  • 示例代碼:
public class SynchronizedDemo4 {
    // 作用于當前類的實例對象
    public synchronized void m1() {
    }

    // 作用于當前類的實例對象
    public synchronized void m2() {
    }

    // 作用于當前類的實例對象
    public void m3() {
        synchronized (this) {
        }
    }

    // 作用于當前類Class對象
    public static synchronized void m4() {
    }

    // 作用于當前類Class對象
    public static void m5() {
        synchronized (SynchronizedDemo4.class) {
        }
    }

    public static class T extends Thread {
        SynchronizedDemo4 demo;

        public T(SynchronizedDemo4 demo) {
            this.demo = demo;
        }

        @Override
        public void run() {
            super.run();
        }
    }

    public static void main(String[] args) {
        SynchronizedDemo4 d1 = new SynchronizedDemo4();
        Thread t1 = new Thread(() -> {
            d1.m1();
        });
        Thread t2 = new Thread(() -> {
            d1.m2();
        });
        
        Thread t3 = new Thread(() -> {
            d1.m3();
        });
        
        SynchronizedDemo4 d2 = new SynchronizedDemo4();
        Thread t4 = new Thread(() -> {
            d2.m2();
        });
        
        Thread t5 = new Thread(() -> {
            SynchronizedDemo4.m4();
        });
        
        Thread t6 = new Thread(() -> {
            SynchronizedDemo4.m5();
        });
        
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
    }
}

結論:

  • 線程t1、t2、t3中調用的方法都需要獲取d1的鎖,所以他們是互斥的
  • t1/t2/t3這3個線程和t4不互斥,他們可以同時運行,因為前面三個線程依賴于d1的鎖,t4依賴于d2的鎖
  • t5、t6都作用于當前類的Class對象鎖,所以這兩個線程是互斥的,和其他幾個線程不互斥

5. synchronized的可重入性

  • 示例代碼:
public class SynchronizedDemo5 {
    synchronized void method1() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        method2();
        System.out.println("method1 thread-" + Thread.currentThread().getName() + " end");
    }

    synchronized void method2() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("method2 thread-" + Thread.currentThread().getName() + " end");
    }

    public static void main(String[] args) {
        SynchronizedDemo5 t5 = new SynchronizedDemo5();
        new Thread(t5::method1, "1").start();
        new Thread(t5::method1, "2").start();
        new Thread(t5::method1, "3").start();
    }
}
  • 打印:

method2 thread-1 end

method1 thread-1 end

method2 thread-3 end

method1 thread-3 end

method2 thread-2 end

method1 thread-2 end

  • 結論:

當線程啟動的時候,已經獲取了對象的鎖,等method1調用method2方法的時候,同樣是拿到了這個對象的鎖。所以synchronized是可重入的。

6. 發(fā)生異常synchronized會釋放鎖

  • 示例代碼:
public class SynchronizedDemo6 {
    int num = 0;
    synchronized void add() {
        System.out.println("thread" + Thread.currentThread().getName() + " start");
        while (num <= 7) {
            num++;
            System.out.println("thread" + Thread.currentThread().getName() + ", num is " + num);
            if (num == 3) {
                throw new NullPointerException();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynchronizedDemo6 synchronizedDemo6 = new SynchronizedDemo6();
        new Thread(synchronizedDemo6::add, "1").start();
        Thread.sleep(1000);
        new Thread(synchronizedDemo6::add, "2").start();
    }
}

打?。?/p>

thread1 start
thread1, num is 1
thread1, num is 2
thread1, num is 3
Exception in thread “1” java.lang.NullPointerException
at com.xin.demo.threaddemo.lockdemo.synchronizeddemo.SynchronizedDemo6.add(SynchronizedDemo6.java:14)
at java.lang.Thread.run(Thread.java:748)
thread2 start
thread2, num is 4
thread2, num is 5
thread2, num is 6
thread2, num is 7
thread2, num is 8

  • 結論:

發(fā)生異常synchronized會釋放鎖

7. synchronized的實現(xiàn)原理與應用(包含鎖的升級過程)

我的另一篇讀書筆記:Java并發(fā)機制的底層實現(xiàn)原理

鎖的升級過程:無鎖->偏向鎖->輕量級鎖->重量級鎖,詳細情況還是看上面這篇文章

  • 無鎖
  • 偏向鎖:在鎖對象的對象頭中記錄一下當前獲取到該鎖的線程ID,該線程下次如果又來獲取該鎖就可以直接獲取到了,也就是支持鎖重入
  • 輕量級鎖:當兩個或以上線程交替獲取鎖,但并沒有在對象上并發(fā)的獲取鎖時,偏向鎖升級為輕量級鎖。在此階段,線程采取CAS的自旋方式嘗試獲取鎖,避免阻塞線程造成的CPU在用戶態(tài)和內核態(tài)間轉換的消耗。輕量級鎖時,CPU是用戶態(tài)。
  • 重量級鎖:兩個或以上線程并發(fā)的在同一個對象上進行同步時,為了避免無用自旋消耗CPU,輕量級鎖會升級成重量級鎖。重量級鎖時,CPU是內核態(tài)。

總結

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • mybatis-plus自帶保存接口,主鍵Id不是從1開始問題及解決

    mybatis-plus自帶保存接口,主鍵Id不是從1開始問題及解決

    文章描述了在使用MyBatis-Plus的save方法保存數(shù)據(jù)時,遇到主鍵ID過大導致報錯的問題,提出的解決方案包括使用@TableId注解讓MyBatis-Plus使用數(shù)據(jù)庫的自增模式,使用truncatetable方法重置表并備份數(shù)據(jù),以及通過建表語句設置主鍵ID的初始值
    2025-12-12
  • java接口性能從20s優(yōu)化到500ms示例詳解

    java接口性能從20s優(yōu)化到500ms示例詳解

    這篇文章主要為大家介紹了java接口性能從20s優(yōu)化到500ms的操作技巧示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • java實現(xiàn)ArrayList根據(jù)存儲對象排序功能示例

    java實現(xiàn)ArrayList根據(jù)存儲對象排序功能示例

    這篇文章主要介紹了java實現(xiàn)ArrayList根據(jù)存儲對象排序功能,結合實例形式分析了java針對ArrayList的相關運算、排序操作技巧,需要的朋友可以參考下
    2018-01-01
  • 使用maven依賴詳解

    使用maven依賴詳解

    本文主要介紹了Maven的基礎知識,包括Maven的簡介、倉庫類型、常用命令、場景舉例、指令總結、依賴范圍、settings.xml說明等,同時,還詳細講解了Maven依賴的傳遞、手動聲明optional、沖突解決等概念
    2025-02-02
  • Java操作XML轉JSON數(shù)據(jù)格式詳細代碼實例

    Java操作XML轉JSON數(shù)據(jù)格式詳細代碼實例

    在Java中我們可以使用一些現(xiàn)成的庫來實現(xiàn)XML到JSON的轉換,下面這篇文章主要給大家介紹了關于Java操作XML轉JSON數(shù)據(jù)格式的相關資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-04-04
  • java發(fā)送http get請求的兩種方式

    java發(fā)送http get請求的兩種方式

    這篇文章主要為大家詳細介紹了java發(fā)送http get請求的兩種方式,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • spring security登錄認證授權的項目實踐

    spring security登錄認證授權的項目實踐

    SpringSecurity主要實現(xiàn)了認證(Authentication)和授權(AccessControl),本文就來介紹一下spring security登錄認證授權的項目實踐,感興趣的可以了解一下
    2025-01-01
  • SpringData?Redis的具體使用

    SpringData?Redis的具體使用

    本文主要介紹了SpringData?Redis的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-04-04
  • Java實現(xiàn)的計時器【秒表】功能示例

    Java實現(xiàn)的計時器【秒表】功能示例

    這篇文章主要介紹了Java實現(xiàn)的計時器【秒表】功能,結合實例形式分析了Java結合JFrame框架的計時器功能相關操作技巧,需要的朋友可以參考下
    2019-02-02
  • java遞歸生成樹型結構方式

    java遞歸生成樹型結構方式

    文章介紹了如何使用Java遞歸生成樹形結構,包括獲取數(shù)據(jù)、生成樹形結構、查詢子節(jié)點等步驟,作者分享了自己的經驗,希望能對大家有所幫助
    2024-12-12

最新評論

苏州市| 天镇县| 安岳县| 泸西县| 浪卡子县| 行唐县| 宕昌县| 台湾省| 阳江市| 茶陵县| 丹凤县| 遂平县| 六盘水市| 芜湖县| 招远市| 四会市| 合山市| 涞水县| 保亭| 天台县| 淅川县| 井冈山市| 屏边| 揭阳市| 福清市| 无棣县| 西充县| 永春县| 赤城县| 海口市| 云林县| 平塘县| 龙门县| 塔城市| 濮阳县| 怀柔区| 防城港市| 霍林郭勒市| 嘉义县| 大新县| 永善县|