欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java多線(xiàn)程之線(xiàn)程安全問(wèn)題詳情

 更新時(shí)間:2022年06月02日 16:33:34   作者:??未見(jiàn)花聞????  
這篇文章主要介紹了Java多線(xiàn)程之線(xiàn)程安全問(wèn)題詳情,線(xiàn)程安全問(wèn)題是指因多線(xiàn)程搶占式執(zhí)行而導(dǎo)致程序出現(xiàn)bug的問(wèn)題。內(nèi)容介紹詳細(xì)內(nèi)容需要的小伙伴可以參考下面文章內(nèi)容

前言:

本篇文章介紹的內(nèi)容為Java多線(xiàn)程中的線(xiàn)程安全問(wèn)題,此處的安全問(wèn)題并不是指的像黑客入侵造成的安全問(wèn)題,線(xiàn)程安全問(wèn)題是指因多線(xiàn)程搶占式執(zhí)行而導(dǎo)致程序出現(xiàn)bug的問(wèn)題。

1.線(xiàn)程安全概述

1.1什么是線(xiàn)程安全問(wèn)題

首先我們需要明白操作系統(tǒng)中線(xiàn)程的調(diào)度是搶占式執(zhí)行的,或者說(shuō)是隨機(jī)的,這就造成線(xiàn)程調(diào)度執(zhí)行時(shí)線(xiàn)程的執(zhí)行順序是不確定的,有一些代碼執(zhí)行順序不同不影響程序運(yùn)行的結(jié)果,但也有一些代碼執(zhí)行順序發(fā)生改變了重寫(xiě)的運(yùn)行結(jié)果會(huì)受影響,這就造成程序會(huì)出現(xiàn)bug,對(duì)于多線(xiàn)程并發(fā)時(shí)會(huì)使程序出現(xiàn)bug的代碼稱(chēng)作線(xiàn)程不安全的代碼,這就是線(xiàn)程安全問(wèn)題。

下面,將介紹一種典型的線(xiàn)程安全問(wèn)題實(shí)例,整數(shù)自增問(wèn)題。

1.2一個(gè)存在線(xiàn)程安全問(wèn)題的程序

有一天,老師布置了這樣一個(gè)問(wèn)題:使用兩個(gè)線(xiàn)程將變量count自增10萬(wàn)次,每個(gè)線(xiàn)程承擔(dān)5萬(wàn)次的自增任務(wù),變量count的初始值為0。 這個(gè)問(wèn)題很簡(jiǎn)單,最終的結(jié)果我們也能夠口算出來(lái),答案就是10萬(wàn)。 小明同學(xué)做事非常迅速,很快就寫(xiě)出了下面的一段代碼:

class Counter {
    private int count;
    public void increase() {
        ++this.count;
    }
    public int getCount() {
        return this.count;
    }
}

public class Main11 {
    private static final int CNT = 50000;
    private static final Counter counter = new Counter();
    public static void main(String[] args) throws InterruptedException {

        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < CNT; i++) {
                counter.increase();
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int j = 0; j < CNT; j++) {
                counter.increase();
            }
        });

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();

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

按理來(lái)說(shuō),結(jié)果應(yīng)該是10萬(wàn),我們來(lái)看看運(yùn)行結(jié)果:

運(yùn)行的結(jié)果比10萬(wàn)要小,你可以試著運(yùn)行該程序你會(huì)發(fā)現(xiàn)每次運(yùn)行的結(jié)果都不一樣,但絕大部分情況,結(jié)果都會(huì)比預(yù)期的值要小,下面我們就來(lái)分析分析為什么會(huì)這樣。

2.線(xiàn)程加鎖與線(xiàn)程不安全的原因

2.1案例分析

上面我們使用多線(xiàn)程運(yùn)行了一個(gè)程序,將一個(gè)變量值為0的變量自增10萬(wàn)次,但是最終實(shí)際結(jié)果比我們預(yù)期結(jié)果要小,原因就是線(xiàn)程調(diào)度的順序是隨機(jī)的,造成線(xiàn)程間自增的指令集交叉,導(dǎo)致運(yùn)行時(shí)出現(xiàn)兩次自增但值只自增一次的情況,所以得到的結(jié)果會(huì)偏小。

我們知道一次自增操作可以包含以下幾條指令:

  • 將內(nèi)存中變量的值加載到寄存器,不妨將該操作記為load。
  • 在寄存器中執(zhí)行自增操作,不妨將該操作記為add。
  • 將寄存器的值保存至內(nèi)存中,不妨將該操作記為save。

我們來(lái)畫(huà)一條時(shí)間軸,來(lái)總結(jié)一下常見(jiàn)的幾種情況:

情況1: 線(xiàn)程間指令集,無(wú)交叉,運(yùn)行結(jié)果與預(yù)期相同,圖中寄存器A表示線(xiàn)程1所用的寄存器,寄存器B表示線(xiàn)程2所用的寄存器,后續(xù)情況同理。

情況2: 線(xiàn)程間指令集存在交叉,運(yùn)行結(jié)果低于預(yù)期結(jié)果。

情況3: 線(xiàn)程間指令集完全交叉,實(shí)際結(jié)果低于預(yù)期。

根據(jù)上面我們所列舉的情況,發(fā)現(xiàn)線(xiàn)程運(yùn)行時(shí)沒(méi)有交叉指令的時(shí)候運(yùn)行結(jié)果是正常的,但是一旦有了交叉會(huì)導(dǎo)致自增操作的結(jié)果會(huì)少1,綜上可以得到一個(gè)結(jié)論,那就是由于自增操作不是原子性的,多個(gè)線(xiàn)程并發(fā)執(zhí)行時(shí)很可能會(huì)導(dǎo)致執(zhí)行的指令交叉,導(dǎo)致線(xiàn)程安全問(wèn)題。

那如何解決上述線(xiàn)程不安全的問(wèn)題呢?當(dāng)然有,那就是對(duì)對(duì)象加鎖。

2.2線(xiàn)程加鎖

2.2.1什么是加鎖

為了解決由于“搶占式執(zhí)行”所導(dǎo)致的線(xiàn)程安全問(wèn)題,我們可以對(duì)操作的對(duì)象進(jìn)行加鎖,當(dāng)一個(gè)線(xiàn)程拿到該對(duì)象的鎖后,會(huì)將該對(duì)象鎖起來(lái),其他線(xiàn)程如果需要執(zhí)行該對(duì)象的任務(wù)時(shí),需要等待該線(xiàn)程運(yùn)行完該對(duì)象的任務(wù)后才能執(zhí)行。

舉個(gè)例子,假設(shè)要你去銀行的ATM機(jī)存錢(qián)或者取款,每臺(tái)ATM機(jī)一般都在一間單獨(dú)的小房子里面,這個(gè)小房子有一扇門(mén)一把鎖,你進(jìn)去使用ATM機(jī)時(shí),門(mén)會(huì)自動(dòng)的鎖上,這個(gè)時(shí)候如果有人要來(lái)取款,那它得等你使用完并出來(lái)它才能進(jìn)去使用ATM,那么這里的“你”相當(dāng)于線(xiàn)程,ATM相當(dāng)于一個(gè)對(duì)象,小房子相當(dāng)于一把鎖,其他的人相當(dāng)于其他的線(xiàn)程。

在java中最常用的加鎖操作就是使用synchronized關(guān)鍵字進(jìn)行加鎖。

2.2.2如何加鎖

synchronized 會(huì)起到互斥效果, 某個(gè)線(xiàn)程執(zhí)行到某個(gè)對(duì)象的 synchronized 中時(shí), 其他線(xiàn)程如果也執(zhí)行到同一個(gè)對(duì)象 synchronized 就會(huì)阻塞等待。 線(xiàn)程進(jìn)入 synchronized 修飾的代碼塊, 相當(dāng)于 加鎖,退出 synchronized 修飾的代碼塊, 相當(dāng)于 解鎖。

java中的加鎖操作可以使用synchronized關(guān)鍵字來(lái)實(shí)現(xiàn),它的常見(jiàn)使用方式如下:

方式1: 使用synchronized關(guān)鍵字修飾普通方法,這樣會(huì)使方法所在的對(duì)象加上一把鎖。 例如,就以上面自增的程序?yàn)槔?,嘗試使用synchronized關(guān)鍵字進(jìn)行加鎖,如下我對(duì)increase方法進(jìn)行了加鎖,實(shí)際上是對(duì)某個(gè)對(duì)象加鎖,此鎖的對(duì)象就是this,本質(zhì)上加鎖操作就是修改this對(duì)象頭的標(biāo)記位。

class Counter {
    private int count;
    synchronized public void increase() {
        ++this.count;
    }
    public int getCount() {
        return this.count;
    }
}

多線(xiàn)程自增的main方法如下,后面會(huì)以相同的栗子介紹synchronized的其他用法,后面就不在列出這段代碼了。

public class Main11 {
    private static final int CNT = 50000;
    private static final Counter counter = new Counter();
    public static void main(String[] args) throws InterruptedException {

        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < CNT; i++) {
                counter.increase();
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int j = 0; j < CNT; j++) {
                counter.increase();
            }
        });

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();

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

看看運(yùn)行結(jié)果:

方式

2: 使用synchronized關(guān)鍵字對(duì)代碼段進(jìn)行加鎖,但是需要顯式指定加鎖的對(duì)象。 例如:

class Counter {
    private int count;
    public void increase() {
        synchronized (this){
            ++this.count;
        }
    }
    public int getCount() {
        return this.count;
    }
}

運(yùn)行結(jié)果:

方式3: 使用synchronized關(guān)鍵字修飾靜態(tài)方法,相當(dāng)于對(duì)當(dāng)前類(lèi)的類(lèi)對(duì)象進(jìn)行加鎖。

class Counter {
    private static int count;
    synchronized public static void increase() {
        ++count;
    }
    public int getCount() {
        return this.count;
    }
}

運(yùn)行結(jié)果:

常見(jiàn)的用法差不多就是這些,對(duì)于線(xiàn)程加鎖(線(xiàn)程拿鎖),如果兩個(gè)線(xiàn)程同時(shí)拿一個(gè)對(duì)象的鎖,就會(huì)產(chǎn)生鎖競(jìng)爭(zhēng),兩個(gè)線(xiàn)程同時(shí)拿兩個(gè)不同對(duì)象的鎖不會(huì)產(chǎn)生鎖競(jìng)爭(zhēng)。 對(duì)于synchronized這個(gè)關(guān)鍵字,它的英文意思是同步,但是同步在計(jì)算機(jī)中是存在多種意思的,比如在多線(xiàn)程中,這里同步的意思是“互斥”;而在IO或網(wǎng)絡(luò)編程中同步指的是“異步”,與多線(xiàn)程沒(méi)有半點(diǎn)的關(guān)系。

synchronized 的工作過(guò)程:

  • 獲得互斥鎖lock
  • 從主內(nèi)存拷貝變量的最新副本到工作的內(nèi)存
  • 執(zhí)行代碼
  • 將更改后的共享變量的值刷新到主內(nèi)存
  • 釋放互斥鎖unlock

synchronized 同步塊對(duì)同一條線(xiàn)程來(lái)說(shuō)是可重入的,不會(huì)出現(xiàn)自己把自己鎖死的問(wèn)題,即死鎖問(wèn)題,關(guān)于死鎖后續(xù)文章再做介紹。

綜上,synchronized關(guān)鍵字加鎖有如下性質(zhì):互斥性,刷新內(nèi)存性,可重入性。synchronized關(guān)鍵字也相當(dāng)于一把監(jiān)視器鎖monitor lock,如果不加鎖,直接使用wait方法(一種線(xiàn)程等待的方法,后面細(xì)說(shuō)),會(huì)拋出非法監(jiān)視器異常,引發(fā)這個(gè)異常的原因就是沒(méi)有加鎖。

2.2.3再析案例

對(duì)自增那個(gè)代碼上鎖后,我們?cè)賮?lái)分析一下為什么加上了所就線(xiàn)程安全了,先列代碼:

class Counter {
    private int count;
    synchronized public void increase() {
        ++this.count;
    }
    public int getCount() {
        return this.count;
    }
}

public class Main11 {
    private static final int CNT = 50000;
    private static final Counter counter = new Counter();
    public static void main(String[] args) throws InterruptedException {

        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < CNT; i++) {
                counter.increase();
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int j = 0; j < CNT; j++) {
                counter.increase();
            }
        });

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();

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

多線(xiàn)程并發(fā)執(zhí)行時(shí),上一次就分析過(guò)沒(méi)有指令集交叉就不會(huì)出現(xiàn)問(wèn)題,因此這里我們只討論指令交叉后,加鎖操作是如何保證線(xiàn)程安全的,不妨記加鎖為lock,解鎖為unlock,兩個(gè)線(xiàn)程運(yùn)行過(guò)程如下: 線(xiàn)程1首先拿到目標(biāo)對(duì)象的鎖,對(duì)對(duì)象進(jìn)行加鎖,處于lock狀態(tài),當(dāng)線(xiàn)程2來(lái)執(zhí)行自增操作時(shí)會(huì)發(fā)生阻塞,直到線(xiàn)程1的自增操作完畢,處于unlock狀態(tài),線(xiàn)程2才會(huì)就緒取執(zhí)行線(xiàn)程2的自增操作。

加鎖后線(xiàn)程就是串行執(zhí)行,與單線(xiàn)程其實(shí)沒(méi)有很大的區(qū)別,那多線(xiàn)程是不是沒(méi)有用了呢?但是對(duì)方法加鎖后,線(xiàn)程運(yùn)行該方法才會(huì)加鎖,運(yùn)行完該方法就會(huì)自動(dòng)解鎖,況且大部分操作并發(fā)執(zhí)行是不會(huì)造成線(xiàn)程安全的,只有少部分的修改操作才會(huì)有可能導(dǎo)致線(xiàn)程安全問(wèn)題,因此整體上多線(xiàn)程運(yùn)行效率還是比單線(xiàn)程高得多。

2.3線(xiàn)程不安全的原因

首先,線(xiàn)程不安全根源是線(xiàn)程間的調(diào)度充滿(mǎn)隨機(jī)性,導(dǎo)致原有的邏輯被改變,造成線(xiàn)程不安全,這個(gè)問(wèn)題無(wú)法解決,無(wú)可奈何。多個(gè)線(xiàn)程針對(duì)同一資源進(jìn)行寫(xiě)(修改)操作,并且針對(duì)資源的修改操作不是原子性的,可能會(huì)導(dǎo)致線(xiàn)程不安全問(wèn)題,類(lèi)似于數(shù)據(jù)庫(kù)的事務(wù)。

由于編譯器的優(yōu)化,內(nèi)存可見(jiàn)性無(wú)法保證,就是當(dāng)線(xiàn)程頻繁地對(duì)同一個(gè)變量進(jìn)行讀操作時(shí),會(huì)直接從寄存器上讀值,不會(huì)從內(nèi)存上讀值,這樣內(nèi)存的值修改時(shí),線(xiàn)程就感知不到該變量已經(jīng)修改,會(huì)導(dǎo)致線(xiàn)程安全問(wèn)題(這是編譯器優(yōu)化的結(jié)果,現(xiàn)代的編譯器都有類(lèi)似的優(yōu)化不止于Java),因?yàn)橄啾扔诩拇嫫鳎瑥膬?nèi)容中讀取數(shù)據(jù)的效率要小的多,所以編譯器會(huì)盡可能地在邏輯不變的情況下對(duì)代碼進(jìn)行優(yōu)化,單線(xiàn)程情況下是不會(huì)翻車(chē)的,但是多線(xiàn)程就不一定了,比如下面一段代碼:

import java.util.Scanner;

public class Main12 {
    private static int isQuit;
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (isQuit == 0) {

            }
            System.out.println("線(xiàn)程thread執(zhí)行完畢!");
        });
        thread.start();

        Scanner sc = new Scanner(System.in);
        System.out.println("請(qǐng)輸入isQuit的值,不為0線(xiàn)程thread停止執(zhí)行!");
        isQuit = sc.nextInt();
        System.out.println("main線(xiàn)程執(zhí)行完畢!");
    }
}

運(yùn)行結(jié)果:

我們從運(yùn)行結(jié)果可以知道,輸入isQuit后,線(xiàn)程thread沒(méi)有停止,這就是編譯器優(yōu)化導(dǎo)致線(xiàn)程感知不到內(nèi)存可見(jiàn)性,從而導(dǎo)致線(xiàn)程不安全。 我們可以使用volatile關(guān)鍵字保證內(nèi)存可見(jiàn)性。 我們可以使用volatile關(guān)鍵字修飾isQuit來(lái)保證內(nèi)存可見(jiàn)性。

import java.util.Scanner;

public class Main12 {
    volatile private static int isQuit;
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (isQuit == 0) {

            }
            System.out.println("線(xiàn)程thread執(zhí)行完畢!");
        });
        thread.start();

        Scanner sc = new Scanner(System.in);
        System.out.println("請(qǐng)輸入isQuit的值,不為0線(xiàn)程thread停止執(zhí)行!");
        isQuit = sc.nextInt();
        System.out.println("main線(xiàn)程執(zhí)行完畢!");
    }
}

運(yùn)行結(jié)果:

synchronized與volatile關(guān)鍵字的區(qū)別: synchronized關(guān)鍵字能保證原子性,但是是否能夠保證內(nèi)存可見(jiàn)性要看情況(上面這個(gè)栗子是不行的),而volatile關(guān)鍵字只能保證內(nèi)存可見(jiàn)性不能保證原子性。 保證內(nèi)存可見(jiàn)性就是禁止編譯器做出如上的優(yōu)化而已。

import java.util.Scanner;

public class Main12 {
    private static int isQuit;
    //鎖對(duì)象
    private static final Object lock = new Object();
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
                synchronized (lock) {
                    while (isQuit == 0) {

                    }
                    System.out.println("線(xiàn)程thread執(zhí)行完畢!");
                }
        });
        thread.start();

        Scanner sc = new Scanner(System.in);
        System.out.println("請(qǐng)輸入isQuit的值,不為0線(xiàn)程thread停止執(zhí)行!");
        isQuit = sc.nextInt();
        System.out.println("main線(xiàn)程執(zhí)行完畢!");
    }
}

運(yùn)行結(jié)果:

編譯器優(yōu)化除了導(dǎo)致內(nèi)存可見(jiàn)性感知不到的問(wèn)題,還有指令重排序也會(huì)導(dǎo)致線(xiàn)程安全問(wèn)題,指令重排序也是編譯器優(yōu)化之一,就是編譯器會(huì)智能地(保證原有邏輯不變的情況下)調(diào)整代碼執(zhí)行順序,從而提高程序運(yùn)行的效率,單線(xiàn)程沒(méi)問(wèn)題,但是多線(xiàn)程可能會(huì)翻車(chē),這個(gè)原因了解即可。

3.線(xiàn)程安全的標(biāo)準(zhǔn)類(lèi)

Java 標(biāo)準(zhǔn)庫(kù)中很多都是線(xiàn)程不安全的。這些類(lèi)可能會(huì)涉及到多線(xiàn)程修改共享數(shù)據(jù), 又沒(méi)有任何加鎖措施。例如,ArrayList,LinkedList,HashMap,TreeMap,HashSet,TreeSet,StringBuilder。 但是還有一些是線(xiàn)程安全的,使用了一些鎖機(jī)制來(lái)控制,例如,Vector (不推薦使用),HashTable (不推薦使用),ConcurrentHashMap (推薦),StringBuffer。 還有的雖然沒(méi)有加鎖, 但是不涉及 "修改", 仍然是線(xiàn)程安全的,例如String。

在線(xiàn)程安全問(wèn)題中可能你還會(huì)遇到JMM模型,在這里補(bǔ)充一下,JMM其實(shí)就是把操作系統(tǒng)中的寄存器,緩存和內(nèi)存重新封裝了一下,其中在JMM中寄存器和緩存稱(chēng)為工作內(nèi)存,內(nèi)存稱(chēng)為主內(nèi)存。 其中緩存分為一級(jí)緩存L1,二級(jí)緩存L2和三級(jí)緩存L3,從L1到L3空間越來(lái)越大,最大也比內(nèi)存空間小,最小也比寄存器空間大,訪(fǎng)問(wèn)速度越來(lái)越慢,最慢也比內(nèi)存的訪(fǎng)問(wèn)速度快,最快也沒(méi)有寄存器訪(fǎng)問(wèn)快。

4.Object類(lèi)提供的線(xiàn)程等待方法

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

序號(hào)方法說(shuō)明
1public final void wait() throws InterruptedException釋放鎖并使線(xiàn)程進(jìn)入WAITING狀態(tài)
2public final native void wait(long timeout) throws InterruptedException;相比于方法1,多了一個(gè)最長(zhǎng)等待時(shí)間
3public final void wait(long timeout, int nanos) throws InterruptedException相比于方法2,等待的最長(zhǎng)時(shí)間精度更大
4public final native void notify();喚醒一個(gè)WAITING狀態(tài)的線(xiàn)程,并加鎖,搭配wait方法使用
5public final native void notifyAll();喚醒所有處于WAITING狀態(tài)的線(xiàn)程,并加鎖(很可能產(chǎn)生鎖競(jìng)爭(zhēng)),搭配wait方法使用

上面介紹synchronized關(guān)鍵字的時(shí)候,如果不對(duì)線(xiàn)程加鎖會(huì)產(chǎn)生非法監(jiān)視異常,我們來(lái)驗(yàn)證一下:

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

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

看看運(yùn)行結(jié)果:

果然拋出了一個(gè)IllegalMonitorStateException,因?yàn)?code>wait方法的執(zhí)行步驟為:先釋放鎖,再使線(xiàn)程等待,你現(xiàn)在都沒(méi)有加鎖,那如何釋放鎖呢?所以會(huì)拋出這個(gè)異常,但是執(zhí)行notify是無(wú)害的。

wait方法常常搭配notify方法搭配一起使用,前者能夠釋放鎖,使線(xiàn)程等待,后者能獲取鎖,使線(xiàn)程繼續(xù)執(zhí)行,這套組合拳的流程圖如下:

現(xiàn)在有兩個(gè)任務(wù)由兩個(gè)線(xiàn)程執(zhí)行,假設(shè)線(xiàn)程2比線(xiàn)程1先執(zhí)行,請(qǐng)寫(xiě)出一個(gè)多線(xiàn)程程序使任務(wù)1在任務(wù)2前面完成,其中線(xiàn)程1執(zhí)行任務(wù)1,線(xiàn)程2執(zhí)行任務(wù)2。 這個(gè)需求可以使用wait/notify來(lái)實(shí)現(xiàn)。

class Task{
    public void task(int i) {
        System.out.println("任務(wù)" + i + "完成!");
    }
}
public class WiteNotify {
    //鎖對(duì)象
    private static final Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            synchronized (lock) {
                Task task1 = new Task();
                task1.task(1);
                //通知線(xiàn)程2線(xiàn)程1的任務(wù)完成
                System.out.println("notify前");
                lock.notify();
                System.out.println("notify后");
            }
        });
        Thread thread2 = new Thread(() -> {
            synchronized (lock) {
                Task task2 = new Task();
                //等待線(xiàn)程1的任務(wù)1執(zhí)行完畢
                System.out.println("wait前");
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                task2.task(2);
                System.out.println("wait后");
            }
        });
        thread2.start();
        Thread.sleep(10);
        thread1.start();
    }
}

運(yùn)行結(jié)果:

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

相關(guān)文章

  • JFreeChart折線(xiàn)圖的生成方法

    JFreeChart折線(xiàn)圖的生成方法

    這篇文章主要為大家詳細(xì)介紹了JFreeChart折線(xiàn)圖的生成方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-06-06
  • Mybatis防止sql注入的實(shí)例

    Mybatis防止sql注入的實(shí)例

    本文通過(guò)實(shí)例給大家介紹了Mybatis防止sql注入的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-06-06
  • Java?PTA?計(jì)算3到7位?水仙花數(shù)實(shí)例

    Java?PTA?計(jì)算3到7位?水仙花數(shù)實(shí)例

    這篇文章主要介紹了Java?PTA?計(jì)算3到7位?水仙花數(shù)實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • logback-spring.xml配置詳解

    logback-spring.xml配置詳解

    這篇文章主要介紹了logback-spring.xml詳解,本文介紹了logback-spring.xml相關(guān)的知識(shí)與概念,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-07-07
  • Spring Boot使用GridFS實(shí)現(xiàn)文件的上傳和下載方式

    Spring Boot使用GridFS實(shí)現(xiàn)文件的上傳和下載方式

    這篇文章主要介紹了Spring Boot使用GridFS實(shí)現(xiàn)文件的上傳和下載方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • mybatis中mapper.xml文件的常用屬性及標(biāo)簽講解

    mybatis中mapper.xml文件的常用屬性及標(biāo)簽講解

    這篇文章主要介紹了mybatis中mapper.xml文件的常用屬性及標(biāo)簽講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • java使用hashMap緩存保存數(shù)據(jù)的方法

    java使用hashMap緩存保存數(shù)據(jù)的方法

    這篇文章主要介紹了java使用hashMap緩存保存數(shù)據(jù)的方法,結(jié)合實(shí)例形式簡(jiǎn)單分析了java基于hashmap讀寫(xiě)緩存數(shù)據(jù)的相關(guān)操作技巧,需要的朋友可以參考下
    2016-08-08
  • SpringBoot實(shí)現(xiàn)阿里云短信發(fā)送的示例代碼

    SpringBoot實(shí)現(xiàn)阿里云短信發(fā)送的示例代碼

    這篇文章主要為大家介紹了如何利用SpringBoot實(shí)現(xiàn)阿里云短信發(fā)送,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定幫助,需要的可以參考一下
    2022-04-04
  • JavaI/O深入學(xué)習(xí)之輸入和輸出

    JavaI/O深入學(xué)習(xí)之輸入和輸出

    這篇文章主要介紹了JavaI/O深入學(xué)習(xí)之輸入和輸出,Java類(lèi)庫(kù)中的I/O類(lèi)分成輸入和輸出兩部分,可以在JDK文檔里的類(lèi)層次結(jié)構(gòu)中查看到。,需要的朋友可以參考下
    2019-06-06
  • Spring事務(wù)注解@Transactional失效的八種場(chǎng)景分析

    Spring事務(wù)注解@Transactional失效的八種場(chǎng)景分析

    最近在開(kāi)發(fā)采用Spring框架的項(xiàng)目中,使用了@Transactional注解,但發(fā)現(xiàn)事務(wù)注解失效了,所以這篇文章主要給大家介紹了關(guān)于Spring事務(wù)注解@Transactional失效的八種場(chǎng)景,需要的朋友可以參考下
    2021-05-05

最新評(píng)論