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

java synchronized加鎖和釋放流程詳解

 更新時間:2025年05月16日 08:40:56   作者:程序黑板報  
這篇文章主要介紹了java synchronized加鎖和釋放流程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

為什么需要加鎖

在多線程環(huán)境中,多個線程同時運行同一個方法時,如果其中有對某一個資源就行修改處理時,可能會存在先后操作的問題,使得邏輯不一致,程序運行的結果不時我們想要的。

線程如何加鎖

這里只講synchronized進行加鎖,并且只進行使用原理的闡述,其他加鎖方式使用另外的篇幅。

加鎖是為了避免多個線程同時進行邏輯處理時,可能會有數(shù)據(jù)不一致等情況從而影響程序的邏輯的準確性。 所以我們可以使用一個對象,給該對象設置一個鎖狀態(tài)標記,其他線程要進行邏輯處理時需要把該狀態(tài)設置成功才能正常進行,不然就阻塞掛起。 這里問題來了,如果是我們直接在代碼中添加一個狀態(tài)標志,那么多線程的情況下設置這個狀態(tài)下可能還是會有同時處理的情況。

這里我們可以依賴java提供的synchronized關鍵字。

java內存布局和監(jiān)視器鎖

剛剛我們提到,可以給對象設置一個鎖狀態(tài)標記,其實vjm已經(jīng)幫我們實現(xiàn)了,我們平常寫的java對象經(jīng)過編譯字節(jié)碼后,是會在內存中添加一個額外的信息的,這里就涉及到另一個概念,java對象的內存布局或者說java對象的數(shù)據(jù)結構。

當我們通過new關鍵字來新建一個對象時,jvm會在堆內存中開辟一塊內存存儲該對象實例,對象實例除了擁有我們自己定義的一些屬性方法等,還會擁有額外的其他的信息。

分為三塊:

  • 對象頭

對象頭中會存儲有hashcode,GC信息,鎖標記等等。

  • 實例數(shù)據(jù)

實例數(shù)據(jù)就是我們自定義的各個字段和方法信息。

  • 填充對齊

簡單理解為虛擬機中存儲一個對象約定對象大小為8字節(jié)的整數(shù)倍,所以如果不夠的話會額外占用一點空間湊數(shù)。

好了,簡單說到這里就ok了,這里可以看到對象在實際運行過程中擁有鎖標記的,這里稱為監(jiān)視器鎖,實際上對象頭的鎖信息會更多,這里只是簡單概括一下。在程序中通過synchronize關鍵字進行加鎖的話,jvm會幫助我們標記該對象是由那個線程占有了,并且保證其他線程不會再擁有,只有當線程釋放了改對象的鎖后才可以重新進行鎖競爭。

同時synchorize關鍵詞能保證操作的對象是直接從內存中獲取的(內存可見性)。

使用方式如下:

public class ThreadTest {

    public static void main(String[] args) {
        Task task = new Task();

        for (int i = 0; i< 50; i++) {
            new Thread(task).run();
        }

        System.out.println(task.getCount());
    }
}

class Task implements Runnable{
    private int count;

    private Object lock = new Object();

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    @Override
    public void run() {
        int total = 0;
        while (++total <= 10000) {
            synchronized (lock) {
                count++;
            }
        }
    }
}

synchronized究竟鎖了誰

synchronized關鍵字的語法規(guī)則是定義在代碼塊中或者在定義方法時。

剛剛我們提到,java對象頭中有鎖標記,所以下面的邏輯就是對lock這個對象進行鎖競爭

while (++total <= 10000) {
    synchronized (lock) {
        count++;
    }
}

而如果我們synchronized是在方法中定義的話,則是對當前類的實例進行鎖競爭,這里就是C1的實例對象,也即是C1 c1 = new C1()中的c1;而如果程序中還有C1 c11 = new C1()的定義,那么是分開競爭的。也即是同一個對象才進行鎖競爭。

class C1{
    private int count;

    public synchronized void run() {
        int total = 0;
        while (++total <= 10000) {
           count++;
        }
    }
}

如果對象的方法是static的,那么進行鎖競爭的是類對象,這個是jvm進行class字節(jié)碼加載時生成的。

class C1{
    private int count;

    public static synchronized void run() {
        int total = 0;
        while (++total <= 10000) {
           count++;
        }
    }
}

至此,我們可以把監(jiān)視器鎖和synchronized關鍵字梳理了一遍。以上的重點信息是:java對象內存布局和監(jiān)視器鎖以及synchronized關鍵字的處理邏輯。如果需要深入可以對各個點進行往下研究。

線程的等待和喚醒

wait()方法

  • 首先我們需要了解wait()方法的繼承體系,他是在Object對象的基類方法,也就是說所有的對象都擁有wait()方法。一個線程調用了java的wait()方法后,當前線程會被阻塞掛起,這里的調用指的是線程里面調用了加鎖對象的wait()方法。
  • 線程被阻塞掛起后是需要喚醒的,下面會講到喚醒方法,但是也可以調用重載方法wait(long timeout),讓線程被阻塞后超過一定時間還沒被喚醒而自動喚醒。

notify()方法

  • notify()方法也是繼承于Object對象。當某個線程調用了加鎖對象的notify方法后,會喚醒之前在該對象進行獲取監(jiān)視器鎖時失敗而被阻塞的線程,如果有多個線程同時被阻塞,notify()方法只會有一個線程被喚醒,如果需要喚醒全部,則可以調用notifyAll()方法。

所以面試中會被問到wait和notify的作用,可以側重的知識點是:

  • 1.調用wait之前一定是獲取到鎖的,所以要保證在synchronized塊中。
  • 2.調用wait后會釋放該對象的鎖。
  • 3.調用notify()方法也要是獲取鎖, 也要保證在synchronized塊中。
  • 4.調用notify()方法喚醒一個線程,調用notifyAll()方法喚醒全部被阻塞線程。
  • 5.調用notify()或者notifyAll()方法只是喚醒了其他被阻塞的線程,他們有了重新競爭鎖的條件,但是當前線程還沒有釋放鎖的,只有調用了wait()方法才會釋放鎖。

用一個生產(chǎn)者消費者模型來看看wait和notify的用法。生產(chǎn)者消費者模型可以簡單理解為有一個容器,當里面沒有數(shù)據(jù)時生產(chǎn)者會往里面添加數(shù)據(jù),滿了則暫停當前的工作等待消費者消費數(shù)據(jù)后通知他繼續(xù)添加。

消費者會往里面拿數(shù)據(jù),沒有了數(shù)據(jù)則暫停工作等待生產(chǎn)者生產(chǎn)了數(shù)據(jù)并通知他繼續(xù)消費。

public static void main(String[] args) {
        Object lock = new Object();
        AtomicInteger counter = new AtomicInteger(0);
        Queue<Integer> queue = new LinkedList<>();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    while (true) {
                        //如果隊列沒有數(shù)據(jù),調用wait()方法,阻塞自己
                        if (queue.isEmpty()) {
                            try {
                                System.out.println("消費者線程阻塞");
                                lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }

                        //如果隊列不為空,消費數(shù)據(jù);如果線程被生產(chǎn)者通過notifyAll()方法喚醒后,線程重新獲取到鎖時是從這里執(zhí)行的
                        System.out.println("消費者線程消費數(shù)據(jù): " + queue.poll());
                        //消費者消費后,喚醒可能由于之前隊列滿了而主動阻塞自己的生產(chǎn)者
                        lock.notifyAll();

                    }

                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    while (true) {
                        //如果隊列數(shù)據(jù)滿了,調用wait()方法,阻塞自己
                        if (queue.size() > 10) {
                            System.out.println("生產(chǎn)者線程阻塞");
                            try {
                                lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }

                        //如果隊列沒有滿,生產(chǎn)數(shù)據(jù); 如果被其他線程喚醒,在下次獲取到鎖的時候生產(chǎn)數(shù)據(jù)
                        System.out.println("生產(chǎn)者線程生產(chǎn)數(shù)據(jù)");
                        queue.add(counter.incrementAndGet());

                        //隊列有數(shù)據(jù)了,喚醒之前可能沒有數(shù)據(jù)而主動祖寺啊自己的消費者
                        lock.notifyAll();
                    }

                }
            }
        }).start();

    }

總結

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

相關文章

  • Java中負數(shù)的絕對值竟然不一定是正數(shù)

    Java中負數(shù)的絕對值竟然不一定是正數(shù)

    這篇文章主要介紹了Java中負數(shù)的絕對值竟然不一定是正數(shù),文中給大家提到Java 中怎么把負數(shù)轉換為正數(shù),需要的朋友可以參考下
    2021-07-07
  • Spring Cloud Alibaba Nacos Config配置中心實現(xiàn)

    Spring Cloud Alibaba Nacos Config配置中心實現(xiàn)

    這篇文章主要介紹了Spring Cloud Alibaba Nacos Config配置中心實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-04-04
  • 使用Java編寫一個輸出九九口訣乘法表的程序

    使用Java編寫一個輸出九九口訣乘法表的程序

    在學習編程的過程中,編寫簡單的程序來實現(xiàn)基本的數(shù)學運算是一個很好的練習,本文將介紹如何使用Java語言編寫一個程序,用于輸出9*9的乘法口訣表,需要的朋友可以參考下
    2026-01-01
  • Java在運行時識別類型信息的方法詳解

    Java在運行時識別類型信息的方法詳解

    這篇文章主要給大家介紹了關于Java在運行時識別類型信息的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考借鑒,下面來一起看看吧
    2019-01-01
  • IDEA左側項目結構被隱藏如何實現(xiàn)顯示

    IDEA左側項目結構被隱藏如何實現(xiàn)顯示

    這篇文章主要介紹了IDEA左側項目結構被隱藏如何實現(xiàn)顯示問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-08-08
  • spring-session自定義序列化方式

    spring-session自定義序列化方式

    這篇文章主要介紹了spring-session自定義序列化方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • 基于Zookeeper實現(xiàn)服務注冊和服務發(fā)現(xiàn)功能

    基于Zookeeper實現(xiàn)服務注冊和服務發(fā)現(xiàn)功能

    無論是采用SOA還是微服務架構,都需要使用服務注冊和服務發(fā)現(xiàn)組件,本文將基于 Zookeeper 實現(xiàn)服務注冊和服務發(fā)現(xiàn)功能,如果跟我一樣有同樣的困惑,希望可以通過本文了解其他組件如何使用 Zookeeper 作為注冊中心的工作原理
    2023-09-09
  • SpringBoot整合RabbitMQ的5種模式實戰(zhàn)

    SpringBoot整合RabbitMQ的5種模式實戰(zhàn)

    本文主要介紹了SpringBoot整合RabbitMQ的5種模式實戰(zhàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • java實現(xiàn)異步線程,回調接口方式

    java實現(xiàn)異步線程,回調接口方式

    這篇文章主要介紹了java實現(xiàn)異步線程,回調接口方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • springboot如何通過session實現(xiàn)單點登入詳解

    springboot如何通過session實現(xiàn)單點登入詳解

    單點登錄(SSO)的定義是在多個應用系統(tǒng)中,用戶只需要登錄一次就可以訪問所有相互信任的應用系統(tǒng),下面這篇文章主要給大家介紹了關于springboot如何通過session實現(xiàn)單點登入的相關資料,需要的朋友可以參考下
    2021-12-12

最新評論

盖州市| 郧西县| 辽阳市| 金华市| 南雄市| 南丰县| 喀喇| 灵璧县| 桐乡市| 炉霍县| 利津县| 滦平县| 建始县| 黔西| 荥阳市| 兴义市| 柏乡县| 铜山县| 柘城县| 自贡市| 文化| 珲春市| 许昌县| 兴业县| 新邵县| 龙门县| 沂水县| 浏阳市| 海南省| 开封县| 金门县| 霸州市| 肥乡县| 当雄县| 横峰县| 多伦县| 景洪市| 博罗县| 军事| 四川省| 东丰县|