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

Java多線程死鎖問題詳解(wait和notify)

 更新時(shí)間:2023年01月05日 10:06:06   作者:bit榮  
線程之間形成相互等待資源的環(huán)時(shí),就會(huì)形成順序死鎖,下面這篇文章主要給大家介紹了關(guān)于Java多線程死鎖問題(wait和notify)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下

一. synchronnized 的特性

1. 互斥性

synchronized 會(huì)起到互斥效果, 這里的互斥其實(shí)很好理解, 一個(gè)線程執(zhí)行到某個(gè)對(duì)象的 synchronized 中時(shí), 此時(shí)就是針對(duì)這個(gè)對(duì)象加鎖了, 而如果此時(shí)其他線程如果也想要使用 synchronized 針對(duì)同一個(gè)對(duì)象進(jìn)行加鎖, 就必須等到該對(duì)象對(duì)象上的鎖釋放掉才行, 這便是互斥的效果了.

2. 可重入性

同一個(gè)線程針對(duì)同一個(gè)對(duì)象, 連續(xù)加鎖兩次, 是否會(huì)有問題; 如果沒問題, 就是可重入的, 如果有問題, 就是不可重入的.

看下面的代碼, 在Java當(dāng)中是可行的.

class Counter {
    public int count = 0;

    synchronized public void add() {
        synchronized (this) {
            count++;
        }
    }
}

這里的鎖對(duì)象是this只要有線程調(diào)用add, 進(jìn)入add方法的時(shí)候,就會(huì)先加鎖(能夠加鎖成功), 緊接著又遇到了代碼塊, 再次嘗試加鎖.

站在this的視角(鎖對(duì)象)它認(rèn)為自己已經(jīng)被另外的線程給占用了, 這里的第二次加鎖是否要阻塞等待呢? 如果這里的第二次獲取鎖成功, 這個(gè)鎖就是可重入的, 如果進(jìn)入阻塞等待的狀態(tài), 就是不可重入的, 此時(shí)如果進(jìn)入了阻塞等待大的狀態(tài), 可想而知, 我們的程序就 “僵住了” , 這也就是是一種死鎖的情況了.

上面的代碼在Java代碼中是很容易出現(xiàn)的, 為了避免上面所說情況的出現(xiàn), Java中 synchronized 就被設(shè)置成可重入的了.

synchronized可重入的特性其實(shí)就是是在鎖對(duì)象里面記錄一下, 當(dāng)前的鎖是哪個(gè)線程持有的, 如果再次加鎖的線程和持有線程是同一個(gè), 就可以獲取鎖, 否則就阻塞等待.

二. 死鎖問題

1. 什么是死鎖

死鎖是指兩個(gè)或兩個(gè)以上的進(jìn)程在執(zhí)行過程中, 由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象, 若無外力作用, 它們都將無法推進(jìn)下去; 此時(shí)稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖, 這些永遠(yuǎn)在互相等待的進(jìn)程稱為死鎖進(jìn)程; 通俗點(diǎn)說, 死鎖就是兩個(gè)或者多個(gè)相互競爭資源的線程, 你等我, 我等你, 你不放我也不放, 這就造成了他們之間的互相等待, 導(dǎo)致了 “永久” 阻塞.

一旦程序出現(xiàn)死鎖, 就會(huì)導(dǎo)致線程無法繼續(xù)執(zhí)行后續(xù)的工作, 程序勢必會(huì)有嚴(yán)重的bug, 而且是死鎖非常隱蔽的, 開發(fā)階段, 不經(jīng)意間, 就會(huì)寫出死鎖代碼, 還不容易測試出來, 所以這就需要我們對(duì)死鎖問題有一定的認(rèn)識(shí)以方便我們以后的調(diào)試和修改.

2. 死鎖的四個(gè)必要條件

  • 互斥使用: 線程1拿到了鎖, 線程2就得進(jìn)入阻塞狀態(tài)(鎖的基本特性).
  • 不可搶占: 線程1拿到鎖之后, 必須是線程1主動(dòng)釋放, 不可能線程1還沒有釋放, 線程2強(qiáng)行獲取到鎖.
  • 請(qǐng)求和保持: 線程1拿到鎖A后, 再去獲取鎖B的時(shí)候, A這把鎖仍然保持, 不會(huì)因?yàn)橐@取鎖B就把A釋放了.
  • 循環(huán)等待: 線程1先獲取鎖A再獲取鎖B, 線程2先獲取鎖B再獲取鎖A, 線程1在獲取鎖B的時(shí)候等待線程2釋放B,同時(shí)線程2在獲取鎖A的時(shí)候等待線程1釋放A.

而在Java代碼中, 前三點(diǎn) synchronized鎖的基本特性, 我們是無法改變的, 循環(huán)等待是這四個(gè)條件里唯一 一個(gè)和代碼結(jié)構(gòu)相關(guān)的, 是我們可以控制的.

3. 常見的死鎖場景及解決

3.1 不可重入造成的死鎖

同一個(gè)線程針對(duì)同一個(gè)對(duì)象, 連續(xù)加鎖兩次, 如果鎖不是可重入鎖, 就會(huì)造成死鎖問題.

最開始介紹synchronized的特性的時(shí)候所說, synchronized具有可重入性, 而在Java中還有一個(gè)ReentrantLock鎖也是可重入鎖, 所以說, 在Java程序中, 不會(huì)出現(xiàn)這種死鎖問題.

3.2 循環(huán)等待的場景

哲學(xué)家就餐問題(多個(gè)線程多把鎖) 場景

有五位沉默的哲學(xué)家圍坐在一張圓桌旁, 每個(gè)哲學(xué)家有兩種狀態(tài).

  1. 思考人生(相當(dāng)于線程的阻塞狀態(tài))
  2. 拿起筷子吃面條(相當(dāng)于線程獲取到鎖然后執(zhí)行一些計(jì)算)

有五只筷子供他們使用, 哲學(xué)家需要拿到左手和右手邊的兩根筷子之后才能吃飯, 吃完后將筷子放下繼續(xù)思考.

由于操作系統(tǒng)隨機(jī)調(diào)度, 這五個(gè)哲學(xué)家, 隨時(shí)都可能想吃面條, 也隨時(shí)可能要思考人生.

假設(shè)出現(xiàn)了極端情況, 同─時(shí)刻, 所有的哲學(xué)家同時(shí)拿起右手的筷子, 哲學(xué)家們需要再拿起左手的筷子才可以吃面條, 而此時(shí)他們發(fā)現(xiàn)沒有筷子可以拿了, 都在等左邊的哲學(xué)家放下筷子, 這里的筷子落實(shí)到程序中就相當(dāng)于鎖, 此時(shí)就陷入了互相阻塞等待的狀態(tài), 這種場景就是典型的因?yàn)檠h(huán)等待造成的死鎖問題.

解決方案

我們可以給按筷子編號(hào), 哲學(xué)家們拿筷子時(shí)需要遵守一個(gè)規(guī)則, 拿筷子需要先拿編號(hào)小的, 再拿編號(hào)大的, 再來看這個(gè)場景, 哲學(xué)家 2, 3, 4, 5 分別拿起了兩手邊編號(hào)為 1, 2, 3, 4 編號(hào)較小的筷子, 而1號(hào)哲學(xué)家想要拿到編號(hào)編號(hào)較小的1號(hào)筷子發(fā)現(xiàn)已經(jīng)被拿走了, 此時(shí)就空出了5號(hào)筷子, 這樣5號(hào)哲學(xué)家就可以拿起5號(hào)筷子去吃面條了, 等5號(hào)哲學(xué)家放下筷子后, 4號(hào)哲學(xué)家就可以拿起4號(hào)筷子去吃面條了, 以此類推…

對(duì)應(yīng)到程序中, 這樣的做法其實(shí)就是在給鎖編號(hào), 然后再按照一個(gè)規(guī)定好的順序來加鎖, 任意線程加多把鎖的時(shí)候, 都讓線程遵守這個(gè)順序, 這樣就解決了互相阻塞等待的問題.

兩個(gè)線程兩把鎖

兩個(gè)線程兩把鎖, t1, t2線程先各自針對(duì)鎖A, 鎖B加鎖, 然后再去獲取對(duì)方的鎖, 此時(shí)雙方就會(huì)陷入僵持狀態(tài), 造成了死鎖問題.

img

這里可以看一下這里舉出來的現(xiàn)實(shí)中的例子來理解這里的場景:

前段時(shí)間疫情還沒有放開的時(shí)候, 走到哪里都離不開健康碼, 某一天這個(gè)健康碼就給給崩了, 手機(jī)上的健康碼沒辦法正常打開了, 于是程序員就趕到公司去修復(fù)這個(gè)bug, 但是在公司樓下被保安攔住了, 保安要求出示健康碼才能上樓, 程序員說: “健康碼出問題了, 我上樓修復(fù)了才能出示健康碼” ; 保安又說: “你出示了健康碼才能上樓”; 此時(shí)場景就陷入了僵持的狀態(tài), 程序員上不了樓, 健康碼也無法修復(fù); 這個(gè)場景就可以類比這里的鎖問題.

觀察下面的代碼及執(zhí)行結(jié)果:

這里的代碼是為了構(gòu)造一個(gè)死鎖的場景, 代碼中的sleep是為了確保兩個(gè)線程先把第一個(gè)鎖拿到, 因?yàn)榫€程是搶占式執(zhí)行的, 如果沒有sleep的作用, 這里的死鎖場景是不容易構(gòu)造出來的.

public class TestDemo14 {
    public static void main(String[] args) {
        Object A = new Object();
        Object B = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (A) {
                System.out.println(Thread.currentThread().getName()+"獲取到了鎖A");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (B) {
                    System.out.println(Thread.currentThread().getName()+"獲取到了鎖B");
                }
            }
        }, "t1");
        Thread t2 = new Thread(() -> {
            synchronized (B) {
                System.out.println(Thread.currentThread().getName()+"獲取到了鎖B");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (A) {
                    System.out.println(Thread.currentThread().getName()+"獲取到了鎖A");
                }
            }
        }, "t2");
        t1.start();
        t2.start();
    }
}

執(zhí)行結(jié)果:

看這里的執(zhí)行結(jié)果, t1線程獲取到了鎖A但并沒有獲取到鎖B, t2線程獲取到了鎖B但并沒有獲取到鎖A, 也就是說t1t2兩個(gè)線程進(jìn)入了相互阻塞的狀態(tài), 線程無法獲去到兩把鎖, 我們可以使用jconsole工具來觀察一下這兩個(gè)線程的狀態(tài), 分析一下是哪里的代碼造成這里死鎖問題的.

可以發(fā)現(xiàn), t1線程此時(shí)是處于BLOCKED狀態(tài)的, 表示獲取鎖, 獲取不到的阻塞狀態(tài); 根據(jù)堆棧跟蹤的信息反映在代碼中是在第14行.

同樣的, t2線程此時(shí)也是處于BLOCKED阻塞狀態(tài)的; 根據(jù)堆棧跟蹤的信息反映在代碼中是在第27行.

上面敘述的是兩個(gè)線程死鎖問題的代碼場景和具體分析, 那么這里的鎖問題如何解決呢?

其實(shí)也不需要特別復(fù)雜的算法, 實(shí)際開發(fā)中只需要解單高效的解決問題即可, 復(fù)雜了反而會(huì)使程序容易出bug, 可能會(huì)引出新的問題, 就比如上面介紹的哲學(xué)家就餐問題通過限制加鎖順序來解決死鎖問題就是一種簡單高效的解決辦法, 而這里也一樣, 也可以通過控制加鎖的順序來解決, 我們讓t1t2兩個(gè)線程都按照相同的順序來獲取鎖, 比如這里規(guī)定先獲取鎖A, 再獲取鎖B, 這樣按照相同的順序去獲取鎖就避免了循環(huán)等待造成的死鎖問題, 代碼如下:

public class TestDemo14 {
    public static void main(String[] args) {
        Object A = new Object();
        Object B = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (A) {
                System.out.println(Thread.currentThread().getName()+"獲取到了鎖A");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (B) {
                    System.out.println(Thread.currentThread().getName()+"獲取到了鎖B");
                }
            }
        }, "t1");
        Thread t2 = new Thread(() -> {
            synchronized (A) {
                System.out.println(Thread.currentThread().getName()+"獲取到了鎖B");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (B) {
                    System.out.println(Thread.currentThread().getName()+"獲取到了鎖A");
                }
            }
        }, "t2");
        t1.start();
        t2.start();
    }
}

最后的執(zhí)行結(jié)果兩個(gè)線程都獲取到了A,B鎖.

三. Object類中提供線程等待的方法

1. 常用方法

除了Thread類中的能夠?qū)崿F(xiàn)線程等待的方法, 如join, sleep, 在Object類中也提供了相關(guān)線程等待的方法.

方法解釋
public final void wait() throws InterruptedException釋放鎖并使線程進(jìn)入WAITING狀態(tài)
public final native void wait(long timeout) throws InterruptedException相比于上面, 多了一個(gè)最長等待時(shí)間
public final void wait(long timeout, int nanos) throws InterruptedException等待的最長時(shí)間精度更大
public final native void notify();隨機(jī)喚醒一個(gè)WAITING狀態(tài)的線程, 并加鎖, 搭配wait方法使用
public final native void notifyAll();喚醒所有處于WAITING狀態(tài)的線程, 并加鎖(很可能產(chǎn)生鎖競爭), 搭配wait方法使用

我們知道由于線程之間的搶占式執(zhí)行和操作系統(tǒng)的隨機(jī)調(diào)度會(huì)導(dǎo)致線程之間執(zhí)行順序是 “隨機(jī)” 的, 但在實(shí)際開發(fā)中很多場景下我們是希望可以協(xié)調(diào)多個(gè)線程之間的執(zhí)行先后順序的.

雖然線程在內(nèi)核里的調(diào)度是隨機(jī)的, 這個(gè)我們是沒辦法改變的, 但是我們可以通過一些api讓線程主動(dòng)阻塞, 主動(dòng)放棄CPU來給別的線程讓路, 以此來控制線程之間的執(zhí)行順序.

Thread類中的joinsleep方法定程度上也能控制線程的執(zhí)行順序, 但通過join和sleep控制并不夠靈活:

  • 使用join, 則必須要t1徹底執(zhí)行完, t2才能執(zhí)行; 如果是希望t1先干50%的活, 就讓t2開始行動(dòng), join就無能為力了.
  • 使用sleep, 指定一個(gè)休眠時(shí)間的, 但是t1執(zhí)行的這些任務(wù), 到底花了多少時(shí)間, 是不好估計(jì)的.

而使用waitnotify可以更好的解決上述的問題.

下面的代碼t線程中沒有使用synchronized進(jìn)行加鎖, 直接調(diào)用了wait方法, 會(huì)產(chǎn)生非法鎖狀態(tài)異常.

public class TestDemo15 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("執(zhí)行完畢!");
        });

        t.start();
        System.out.println("wait前");
        t.wait();
        System.out.println("wait后");
    }
}

執(zhí)行結(jié)果:

之所以這里會(huì)拋出這個(gè)異常, 是因?yàn)?code>wait方法的執(zhí)行步驟為:

  • 先釋放鎖
  • 再讓線程阻塞等待
  • 最后滿足條件后, 重新嘗試獲取鎖, 并在獲取到鎖后, 繼續(xù)往下執(zhí)行

而上面的代碼都沒有加鎖, 又怎么能釋放鎖鎖呢, 所以會(huì)拋出異常, 所以說, wait操作需要搭配synchronized來使用.

所以對(duì)上面的代碼做出如下修改即可,

synchronized (t) {
    System.out.println("wait前");
    t.wait();
    System.out.println("wait后");
}

執(zhí)行結(jié)果:

2. wait和notify的搭配使用

wait方法常常搭配notify方法搭配一起使用, notify方法用來喚醒wait等待的線程, wait能夠釋放鎖, 使線程等待, 而notify喚醒線程后能夠獲取鎖, 然后使線程繼續(xù)執(zhí)行, 執(zhí)行流程如下:

img

在Java中, notify方法也需要在加鎖前提下使用.

代碼示例:

public class TestDemo16 {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();

        Thread t1 = new Thread(() -> {
            // 這個(gè)線程負(fù)責(zé)進(jìn)行等待
            System.out.println("t1: wait 之前");
            try {
                synchronized (object) {
                    object.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1: wait 之后");
        });

        Thread t2 = new Thread(() -> {
            System.out.println("t2: notify 之前");
            synchronized (object) {
                // notify 務(wù)必要獲取到鎖, 才能進(jìn)行通知
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                object.notify();
            }
            System.out.println("t2: notify 之后");
        });

        t1.start();
        // 此處寫的 sleep 500 是大概率會(huì)讓當(dāng)前的 t1 先執(zhí)行 wait 的.
        // 極端情況下 (電腦特別卡的時(shí)候), 可能線程的調(diào)度時(shí)間就超過了 500 ms
        // 還是可能 t2 先執(zhí)行 notify.
        Thread.sleep(500);
        t2.start();
    }
}

執(zhí)行結(jié)果:

注意事項(xiàng):

雖然這里wait是阻塞了, 阻塞在synchronized代碼塊里, 實(shí)際上, 這里的阻塞是釋放了鎖的, 此時(shí)其他線程是可以獲取到object這個(gè)對(duì)象的鎖的, 這里的阻塞,就處在WAITING狀態(tài).

img

代碼中的鎖對(duì)象和調(diào)用wait, notify方法的對(duì)象必須是相同的才能夠起到應(yīng)有的效果, notify只能喚醒在同一個(gè)對(duì)象上等待的線程.

代碼中要保證先執(zhí)行wait, 后執(zhí)行notify才是有意義的.

wait無參數(shù)版本, 是一個(gè)死等的版本, 只要不進(jìn)行notify, 就會(huì)死等下去, 可以采用wait帶參數(shù)版本設(shè)計(jì)代碼避免死等可能出現(xiàn)的問題.

3. wait 和 sleep 的區(qū)別

  • 相同點(diǎn)
    • 都可以使線程暫停一段時(shí)間來控制線程之間的執(zhí)行順序.
    • wait可以設(shè)置一個(gè)最長等待時(shí)間, 和sleep一樣都可以提前喚醒.
  • 不同點(diǎn)
    • wait是Object類中的一個(gè)方法, sleep是Thread類中的一個(gè)方法.
    • wait必須在synchronized修飾的代碼塊或方法中使用, sleep方法可以在任何位置使用.
    • wait被調(diào)用后當(dāng)前線程進(jìn)入BLOCK狀態(tài)并釋放鎖,并可以通過notify和notifyAll方法進(jìn)行喚醒;sleep被調(diào)用后當(dāng)前線程進(jìn)入TIMED_WAIT狀態(tài),不涉及鎖相關(guān)的操作.
    • 使用sleep只能指定一個(gè)固定的休眠時(shí)間, 線程中執(zhí)行操作的執(zhí)行時(shí)間是無法確定的; 而使用wait在指定操作位置就可以喚醒線程.
    • sleep和wait都可以被提前喚醒, interruppt喚醒sleep, 是會(huì)報(bào)異常的, 這種方式是一個(gè)非正常的執(zhí)行邏輯; 而noitify喚醒wait是正常的業(yè)務(wù)執(zhí)行邏輯, 不會(huì)有任何異常.

4. 練習(xí): 順序打印ABC

有三個(gè)線程, 分別只能打印A, B, C, 實(shí)現(xiàn)代碼控制三個(gè)線程固定按照ABC的順序打印.

public class TestdDemo17 {
    public static void main(String[] args) throws InterruptedException {
        Object locker1 = new Object();
        Object locker2 = new Object();
        Thread t1 = new Thread(() -> {
            System.out.println("A");
            synchronized (locker1) {
                locker1.notify();
            }
        });
        
        Thread t2 = new Thread(() -> {
            synchronized (locker1) {
                try {
                    locker1.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.println("B");

            synchronized (locker2) {
                locker2.notify();
            }
        });
        
        Thread t3 = new Thread(() -> {
            synchronized (locker2) {
                try {
                    locker2.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("C");
        });

        t2.start();
        t3.start();
        Thread.sleep(100);
        t1.start();
    }
}

執(zhí)行結(jié)果:

總結(jié)

到此這篇關(guān)于Java多線程死鎖問題的文章就介紹到這了,更多相關(guān)Java多線程死鎖問題內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 實(shí)例解析使用Java實(shí)現(xiàn)基本的音頻播放器的編寫要點(diǎn)

    實(shí)例解析使用Java實(shí)現(xiàn)基本的音頻播放器的編寫要點(diǎn)

    這篇文章主要介紹了使用Java實(shí)現(xiàn)基本的音頻播放器的代碼要點(diǎn)實(shí)例分享,包括音頻文件的循環(huán)播放等功能實(shí)現(xiàn)的關(guān)鍵點(diǎn),需要的朋友可以參考下
    2016-01-01
  • 詳解java安全編碼指南之可見性和原子性

    詳解java安全編碼指南之可見性和原子性

    java類中會(huì)定義很多變量,有類變量也有實(shí)例變量,這些變量在訪問的過程中,會(huì)遇到一些可見性和原子性的問題。這里我們來詳細(xì)了解一下怎么避免這些問題。
    2021-06-06
  • springboot配置文件中敏感數(shù)據(jù)(賬號(hào)密碼)加密方式

    springboot配置文件中敏感數(shù)據(jù)(賬號(hào)密碼)加密方式

    這篇文章主要介紹了springboot配置文件中敏感數(shù)據(jù)(賬號(hào)密碼)加密方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • Java中如何控制bean的加載順序

    Java中如何控制bean的加載順序

    springboot遵從約定大于配置的原則,極大程度的解決了配置繁瑣的問題,在此基礎(chǔ)上,又提供了spi機(jī)制,用spring.factories可以完成一個(gè)小組件的自動(dòng)裝配功能,這篇文章主要介紹了如何控制bean的加載順序,需要的朋友可以參考下
    2024-12-12
  • java語言自行實(shí)現(xiàn)ULID過程底層原理詳解

    java語言自行實(shí)現(xiàn)ULID過程底層原理詳解

    這篇文章主要為大家介紹了java語言自行實(shí)現(xiàn)ULID過程底層原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • SpringBoot 統(tǒng)一公共返回類的實(shí)現(xiàn)

    SpringBoot 統(tǒng)一公共返回類的實(shí)現(xiàn)

    本文主要介紹了SpringBoot 統(tǒng)一公共返回類的實(shí)現(xiàn),配置后臺(tái)的統(tǒng)一公共返回類,這樣做目的是為了統(tǒng)一返回信息,文中示例代碼介紹的很詳細(xì),感興趣的可以了解一下
    2022-01-01
  • springboot中通過lua腳本來獲取序列號(hào)的方法

    springboot中通過lua腳本來獲取序列號(hào)的方法

    這篇文章主要介紹了springboot中通過lua腳本來獲取序列號(hào)的方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-06-06
  • 使用RestTemplate調(diào)用https接口跳過證書驗(yàn)證

    使用RestTemplate調(diào)用https接口跳過證書驗(yàn)證

    這篇文章主要介紹了使用RestTemplate調(diào)用https接口跳過證書驗(yàn)證,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • springboot+nginx+https+linux實(shí)現(xiàn)負(fù)載均衡加域名訪問簡單測試

    springboot+nginx+https+linux實(shí)現(xiàn)負(fù)載均衡加域名訪問簡單測試

    這篇文章主要介紹了springboot+nginx+https+linux實(shí)現(xiàn)負(fù)載均衡加域名訪問簡單測試,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下
    2019-05-05
  • JVM完全解讀之Metaspace解密源碼分析

    JVM完全解讀之Metaspace解密源碼分析

    通過這篇文章,你將可以了解到,為什么會(huì)有metaspace?metaspace的組成,metaspace的VM參數(shù),jstat里我們應(yīng)該關(guān)注metaspace的哪些值,有需要的朋友可以借鑒參考下
    2022-01-01

最新評(píng)論

从化市| 遵化市| 黄龙县| 观塘区| 古蔺县| 白城市| 黔西| 景德镇市| 衢州市| 若羌县| 娄烦县| 福建省| 图木舒克市| 五寨县| 通城县| 雷州市| 辽中县| 安康市| 浙江省| 获嘉县| 涡阳县| 海兴县| 图木舒克市| 丰原市| 阿拉善盟| 三河市| 柯坪县| 宜宾市| 治县。| 扶余县| 靖边县| 威宁| 榆林市| 康定县| 新化县| 吉水县| 璧山县| 永定县| 乌兰察布市| 镇原县| 古交市|