国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java線程匯總

Lsnsh / 2025人閱讀

摘要:線程需要避免竟態(tài),死鎖以及很多其他共享狀態(tài)的并發(fā)性問(wèn)題。用戶線程在前臺(tái),守護(hù)線程在后臺(tái)運(yùn)行,為其他前臺(tái)線程提供服務(wù)。當(dāng)所有前臺(tái)線程都退出時(shí),守護(hù)線程就會(huì)退出。線程阻塞等待獲取某個(gè)對(duì)象鎖的訪問(wèn)權(quán)限。

1、多線程介紹 多線程優(yōu)點(diǎn)

資源利用率好

程序設(shè)計(jì)簡(jiǎn)單

服務(wù)器響應(yīng)更快

多線程缺點(diǎn)

設(shè)計(jì)更復(fù)雜

上下文切換的開(kāi)銷

增加資源消耗
線程需要內(nèi)存維護(hù)本地的堆棧,同時(shí)需要操作系統(tǒng)資源管理線程。

2、并發(fā)模型

并發(fā)系統(tǒng)可以有多種并發(fā)模型,不同的并發(fā)模型在處理任務(wù)時(shí),線程間的協(xié)作和交互的方式也不同。

并行工作者

委托者將任務(wù)分配到不同的現(xiàn)場(chǎng)去執(zhí)行,每個(gè)工作者完成整個(gè)任務(wù)。工作者們并行運(yùn)作在不同的線程上,甚至可能在不同的CPU上。如圖所示:

優(yōu)點(diǎn):很容易理解和使用。
缺點(diǎn):

共享狀態(tài)會(huì)很復(fù)雜

共享的工作者經(jīng)常需要訪問(wèn)一些共享數(shù)據(jù),無(wú)論是內(nèi)存中的或者共享的數(shù)據(jù)庫(kù)中的。

在并行工作者模型中,線程需要以某種方式存取共享數(shù)據(jù),以確保某個(gè)線程的修改能夠?qū)ζ渌€程可見(jiàn)。線程需要避免竟態(tài),死鎖以及很多其他共享狀態(tài)的并發(fā)性問(wèn)題。

無(wú)狀態(tài)的工作者
共享狀態(tài)能夠被系統(tǒng)中得其他線程修改。所以工作者在每次需要的時(shí)候必須重讀狀態(tài),以確保每次都能訪問(wèn)到最新的副本,不管共享狀態(tài)是保存在內(nèi)存中的還是在外部數(shù)據(jù)庫(kù)中。工作者無(wú)法在內(nèi)部保存這個(gè)狀態(tài)(但是每次需要的時(shí)候可以重讀)稱為無(wú)狀態(tài)的。

每次都重讀需要的數(shù)據(jù),將會(huì)導(dǎo)致速度變慢,特別是狀態(tài)保存在外部數(shù)據(jù)庫(kù)中的時(shí)候。

任務(wù)順序是不確定的
作業(yè)執(zhí)行順序是不確定的。無(wú)法保證哪個(gè)作業(yè)最先或者最后被執(zhí)行。

流水線模式

類似于工廠中生產(chǎn)線上的工人們那樣組織工作者。每個(gè)工作者只負(fù)責(zé)作業(yè)中的部分工作。當(dāng)完成了自己的這部分工作時(shí)工作者會(huì)將作業(yè)轉(zhuǎn)發(fā)給下一個(gè)工作者。每個(gè)工作者在自己的線程中運(yùn)行,并且不會(huì)和其他工作者共享狀態(tài)。有時(shí)也被成為無(wú)共享并行模型

通常使用非阻塞的IO來(lái)設(shè)計(jì)使用流水線并發(fā)模型的系統(tǒng)。非阻塞IO就是,一旦某個(gè)工作者開(kāi)始一個(gè)IO操作的時(shí)候(比如讀取文件或從網(wǎng)絡(luò)連接中讀取數(shù)據(jù)),這個(gè)工作者不會(huì)一直等待IO操作的結(jié)束。IO操作速度很慢,所以等待IO操作結(jié)束很浪費(fèi)CPU時(shí)間。此時(shí)CPU可以做一些其他事情。當(dāng)IO操作完成的時(shí)候,IO操作的結(jié)果(比如讀出的數(shù)據(jù)或者數(shù)據(jù)寫完的狀態(tài))被傳遞給下一個(gè)工作者。

在實(shí)際過(guò)程中,可能會(huì)是這樣:

也可能是這樣:

當(dāng)然還會(huì)有更復(fù)雜的設(shè)計(jì),……

缺點(diǎn): 代碼編寫復(fù)雜,追蹤某個(gè)作業(yè)到底被什么代碼執(zhí)行難度較大。
優(yōu)點(diǎn):

無(wú)需共享的狀態(tài)

工作者之間無(wú)需共享狀態(tài),無(wú)需考慮所有因并發(fā)訪問(wèn)共享對(duì)象而產(chǎn)生的并發(fā)性問(wèn)題,基本上是一個(gè)單線程的實(shí)現(xiàn)。

有狀態(tài)的工作者

當(dāng)工作者知道了沒(méi)有其他線程可以修改它們的數(shù)據(jù),工作者可以變成有狀態(tài)的。對(duì)于有狀態(tài),是指,可以在內(nèi)存中保存它們需要操作的數(shù)據(jù),只需在最后將更改寫回到外部存儲(chǔ)系統(tǒng)。因此,有狀態(tài)的工作者通常比無(wú)狀態(tài)的工作者具有更高的性能。

較好的硬件整合(Hardware Conformity)

當(dāng)能確定代碼只在單線程模式下執(zhí)行的時(shí)候,通常能夠創(chuàng)建更優(yōu)化的數(shù)據(jù)結(jié)構(gòu)和算法。單線程有狀態(tài)的工作者能夠在內(nèi)存中緩存數(shù)據(jù),訪問(wèn)緩存的數(shù)據(jù)變得更快。

合理的作業(yè)順序

基于流水線并發(fā)模型實(shí)現(xiàn)的并發(fā)系統(tǒng),在某種程度上是有可能保證作業(yè)的順序的。作業(yè)的有序性使得它更容易地推出系統(tǒng)在某個(gè)特定時(shí)間點(diǎn)的狀態(tài)。更進(jìn)一步,你可以將所有到達(dá)的作業(yè)寫入到日志中去。一旦這個(gè)系統(tǒng)的某一部分掛掉了,該日志就可以用來(lái)重頭開(kāi)始重建系統(tǒng)當(dāng)時(shí)的狀態(tài)。按照特定的順序?qū)⒆鳂I(yè)寫入日志,并按這個(gè)順序作為有保障的作業(yè)順序。

Actors

在Actor模型中每個(gè)工作者被稱為actor。Actor之間可以直接異步地發(fā)送和處理消息。Actor可以被用來(lái)實(shí)現(xiàn)一個(gè)或多個(gè)像前文描述的那樣的作業(yè)處理流水線。下圖給出了Actor模型:

Channels

工作者之間不直接進(jìn)行通信。相反,它們?cè)诓煌耐ǖ乐邪l(fā)布自己的消息(事件)。其他工作者們可以在這些通道上監(jiān)聽(tīng)消息,發(fā)送者無(wú)需知道誰(shuí)在監(jiān)聽(tīng)。下圖給出了Channel模型:

channel模型對(duì)于來(lái)說(shuō)似乎更加靈活。一個(gè)工作者無(wú)需知道誰(shuí)在后面的流水線上處理作業(yè)。只需知道作業(yè)(或消息等)需要轉(zhuǎn)發(fā)給哪個(gè)通道。通道上的監(jiān)聽(tīng)者可以隨意訂閱或者取消訂閱,并不會(huì)影響向這個(gè)通道發(fā)送消息的工作者。這使得工作者之間具有松散的耦合。

3、實(shí)現(xiàn)多線程方式

多線程實(shí)現(xiàn)方法有兩種:

繼承Thread類

public class MyThread extends Thread {
   public void run(){
     System.out.println("MyThread running");
   }
}

//調(diào)用
MyThread myThread = new MyThread();
myTread.start();

實(shí)現(xiàn)Runnble接口

public class MyRunnable implements Runnable {
   public void run(){
    System.out.println("MyRunnable running");
   }
}

//調(diào)用
Thread thread = new Thread(new MyRunnable());
thread.start();

實(shí)現(xiàn)Runnble接口比Thread類的優(yōu)勢(shì):

可以避免Java單繼承帶來(lái)的局限

增強(qiáng)程序健壯性,能夠被多個(gè)線程共享,代碼和數(shù)據(jù)是獨(dú)立的

適合多個(gè)相同程序代碼的線程區(qū)處理同一資源

Thread中,start和run的區(qū)別:run是在當(dāng)前線程運(yùn)行,start是開(kāi)辟新的線程運(yùn)行!所以一般情況下使用的是start!
執(zhí)行完run()方法后,或在run()方法中return,線程便自然消亡。

線程中斷

當(dāng)一個(gè)線程運(yùn)行時(shí),另一個(gè)線程可以調(diào)用對(duì)應(yīng)的 Thread 對(duì)象的 interrupt()方法來(lái)中斷它,該方法只是在目標(biāo)線程中設(shè)置一個(gè)標(biāo)志,表示它已經(jīng)被中斷,并立即返回。這里需要注意的是,如果只是單純的調(diào)用 interrupt()方法,線程并沒(méi)有實(shí)際被中斷,會(huì)繼續(xù)往下執(zhí)行。

sleep()方法的實(shí)現(xiàn)檢查到休眠線程被中斷,它會(huì)相當(dāng)友好地終止線程,并拋出 InterruptedException 異常。

public class SleepInterrupt extends Object implements Runnable{  
    public void run(){  
        try{  
            System.out.println("in run() - about to sleep for 20 seconds");  
            Thread.sleep(20000);  
            System.out.println("in run() - woke up");  
        }catch(InterruptedException e){  
            System.out.println("in run() - interrupted while sleeping");  
            //處理完中斷異常后,返回到run()方法人口,  
            //如果沒(méi)有return,線程不會(huì)實(shí)際被中斷,它會(huì)繼續(xù)打印下面的信息  
            return;    
        }  
        System.out.println("in run() - leaving normally");  
    }  

    public static void main(String[] args) {  
        SleepInterrupt si = new SleepInterrupt();  
        Thread t = new Thread(si);  
        t.start();  
        //主線程休眠2秒,從而確保剛才啟動(dòng)的線程有機(jī)會(huì)執(zhí)行一段時(shí)間  
        try {  
            Thread.sleep(2000);   
        }catch(InterruptedException e){  
            e.printStackTrace();  
        }  
        System.out.println("in main() - interrupting other thread");  
        //中斷線程t  
        t.interrupt();  
        System.out.println("in main() - leaving");  
    }  
} 

如果將 catch 塊中的 return 語(yǔ)句注釋掉,則線程在拋出異常后,會(huì)繼續(xù)往下執(zhí)行,而不會(huì)被中斷,從而會(huì)打印出leaving normally信息。

待決中斷

另外一種情況,如果線程在調(diào)用 sleep()方法前被中斷,那么該中斷稱為待決中斷,它會(huì)在剛調(diào)用 sleep()方法時(shí),立即拋出 InterruptedException 異常。

public class PendingInterrupt extends Object {  
    public static void main(String[] args){  
        //如果輸入了參數(shù),則在mian線程中中斷當(dāng)前線程(亦即main線程)  
        if( args.length > 0 ){  
            Thread.currentThread().interrupt();  
        }   
        //獲取當(dāng)前時(shí)間  
        long startTime = System.currentTimeMillis();  
        try{  
            Thread.sleep(2000);  
            System.out.println("was NOT interrupted");  
        }catch(InterruptedException x){  
            System.out.println("was interrupted");  
        }  
        //計(jì)算中間代碼執(zhí)行的時(shí)間  
        System.out.println("elapsedTime=" + ( System.currentTimeMillis() - startTime));  
    }  
}

這種模式下,main 線程中斷它自身。除了將中斷標(biāo)志(它是 Thread 的內(nèi)部標(biāo)志)設(shè)置為 true 外,沒(méi)有其他任何影響。線程被中斷了,但 main 線程仍然運(yùn)行,main 線程繼續(xù)監(jiān)視實(shí)時(shí)時(shí)鐘,并進(jìn)入 try 塊,一旦調(diào)用 sleep()方法,它就會(huì)注意到待決中斷的存在,并拋出 InterruptException。

中斷狀態(tài)判斷

isInterrupted()方法判斷是否中斷

Thread.interrupted()方法判斷中斷狀態(tài)

join & yield

join 方法用線程對(duì)象調(diào)用,如果在一個(gè)線程 A 中調(diào)用另一個(gè)線程 B 的 join 方法,線程 A 將會(huì)等待線程 B 執(zhí)行完畢后再執(zhí)行。

yield 可以直接用 Thread 類調(diào)用,yield 讓出 CPU 執(zhí)行權(quán)給同等級(jí)的線程,如果沒(méi)有相同級(jí)別的線程在等待 CPU 的執(zhí)行權(quán),則該線程繼續(xù)執(zhí)行。

守護(hù)線程

Java有兩類線程:UserThread(用戶線程)、Daemon Thread(守護(hù)線程)。
用戶線程在前臺(tái),守護(hù)線程在后臺(tái)運(yùn)行,為其他前臺(tái)線程提供服務(wù)。當(dāng)所有前臺(tái)線程都退出時(shí),守護(hù)線程就會(huì)退出。如果有前臺(tái)線程仍然存活,守護(hù)線程就不會(huì)退出。
守護(hù)線程并非只有虛擬機(jī)內(nèi)部提供,用戶可以使用Thread.setDaemon(true)方法設(shè)置為當(dāng)前線程為守護(hù)線程。

setDaemon(true)必須在調(diào)用的線程的start()方法之前設(shè)置,否則會(huì)拋出異常。

在守護(hù)線程中產(chǎn)生的新線程也是守護(hù)線程

線程阻塞

線程在以下四種狀態(tài)下會(huì)產(chǎn)生阻塞:

執(zhí)行Thread.sleep()

當(dāng)線程遇見(jiàn)wait()語(yǔ)句,它會(huì)一直阻塞到接到通知notify()

線程阻塞與不同的I/O的方式有多種。例:InputStreamread方法,一直阻塞到從流中讀取一個(gè)字節(jié)的數(shù)據(jù)為知。

線程阻塞等待獲取某個(gè)對(duì)象鎖的訪問(wèn)權(quán)限。

4、線程安全

定義:當(dāng)多個(gè)線程訪問(wèn)某個(gè)類時(shí),這個(gè)類始終都能表現(xiàn)出正確的行為,那么這個(gè)類就是線程安全的!

競(jìng)態(tài)條件 & 臨界區(qū)

當(dāng)兩個(gè)線程競(jìng)爭(zhēng)同一資源時(shí),如果對(duì)資源的訪問(wèn)順序敏感,就稱存在競(jìng)態(tài)條件。
導(dǎo)致競(jìng)態(tài)條件發(fā)生的代碼區(qū)稱作:臨界區(qū)。

下例中add()方法就是一個(gè)臨界區(qū),它會(huì)產(chǎn)生競(jìng)態(tài)條件。在臨界區(qū)中使用適當(dāng)?shù)耐骄涂梢员苊飧?jìng)態(tài)條件。

public class Counter {
    protected long count = 0;
    public void add(long value){
        this.count = this.count + value;   
    }
}
數(shù)據(jù)安全

線程逃逸規(guī)則:如果一個(gè)資源的創(chuàng)建,使用,銷毀都在同一個(gè)線程內(nèi)完成,且永遠(yuǎn)不會(huì)脫離該線程的控制,則該資源的使用就是線程安全的。

屬性 描述 是否線程安全
局部變量 在棧中,不會(huì)被線程共享 線程安全
局部對(duì)象 引用所指的對(duì)象都存在共享堆中,對(duì)象不會(huì)被其它方法獲得,也不會(huì)被非局部變量引用到 線程安全
對(duì)象成員 多個(gè)線程執(zhí)行讀操作,或者每個(gè)線程的對(duì)象都相互獨(dú)立 線程安全
局部對(duì)象 對(duì)象會(huì)被其它方法獲得,或者被全局變量引用到 線程非安全
對(duì)象成員 存儲(chǔ)在堆上。若多個(gè)線程同時(shí)更新同一個(gè)對(duì)象的同一個(gè)成員 線程非安全
線程安全

當(dāng)多個(gè)線程同時(shí)訪問(wèn)同一個(gè)資源,并且其中的一個(gè)或者多個(gè)線程對(duì)這個(gè)資源進(jìn)行了寫操作,才會(huì)產(chǎn)生競(jìng)態(tài)條件。多個(gè)線程同時(shí)讀同一個(gè)資源不會(huì)產(chǎn)生競(jìng)態(tài)條件。

我們可以通過(guò)創(chuàng)建不可變的共享對(duì)象來(lái)保證對(duì)象在線程間共享時(shí)不會(huì)被修改,從而實(shí)現(xiàn)線程安全,如下所示:

public class ImmutableValue{
    private int value = 0;

    public ImmutableValue(int value){
        this.value = value;
    }

    public int getValue(){
        return this.value;
    }
}

如果非要對(duì)ImmutableValue進(jìn)行操作的話,可以創(chuàng)建新的實(shí)例進(jìn)行隔離:

public class ImmutableValue{
    private int value = 0;

    public ImmutableValue(int value){
        this.value = value;
    }

    public int getValue(){
        return this.value;
    }

    //創(chuàng)建一個(gè)新的實(shí)例
    public ImmutableValue add(int valueToAdd){
        return new ImmutableValue(this.value + valueToAdd);
    }
}

ImmutableValue可以看做是線程安全的,但是如果別的類引用了ImmutableValue,就不能保證線程安全了。如下所示:

public void Calculator{
    private ImmutableValue currentValue = null;

    public ImmutableValue getValue(){
        return currentValue;
    }

    public void setValue(ImmutableValue newValue){
        this.currentValue = newValue;
    }

    public void add(int newValue){
        this.currentValue = this.currentValue.add(newValue);
    }
}

即使Calculator類內(nèi)部使用了一個(gè)不可變對(duì)象,但Calculator類本身還是可變的,因此Calculator類不是線程安全的。換句話說(shuō):ImmutableValue類是線程安全的,但使用它的類不是。

5、同步(synchronized)

當(dāng)多個(gè)線程訪問(wèn)某個(gè)狀態(tài)變量,并且有線程執(zhí)行寫入操作時(shí),必須采用同步機(jī)制來(lái)協(xié)同這些線程對(duì)變量的訪問(wèn)。

Java的主要同步機(jī)制有:

synchronized關(guān)鍵字

volatile類型變量

顯示鎖

原子變量

無(wú)論是同步方法,還是同步塊都是只針對(duì)同一個(gè)對(duì)象的多線程而言的,只有同一個(gè)對(duì)象產(chǎn)生的多線程,才會(huì)考慮到同步方法或者是同步塊。

實(shí)例方法

Java實(shí)例方法同步是同步在對(duì)象上。這樣,每個(gè)方法同步都同步在方法所屬的實(shí)例。只有一個(gè)線程能夠在實(shí)例方法同步塊中運(yùn)行。如果有多個(gè)實(shí)例存在,那么一個(gè)線程一次可以在一個(gè)實(shí)例同步塊中執(zhí)行操作。一個(gè)實(shí)例一個(gè)線程。

 public synchronized void add(int value){
    this.count += value;
 }
靜態(tài)方法同步

靜態(tài)方法的同步是指同步在該方法所在的類對(duì)象上。因?yàn)樵贘ava虛擬機(jī)中一個(gè)類只能對(duì)應(yīng)一個(gè)類對(duì)象,所以同時(shí)只允許一個(gè)線程執(zhí)行同一個(gè)類中的靜態(tài)同步方法。

對(duì)于不同類中的靜態(tài)同步方法,一個(gè)線程可以執(zhí)行每個(gè)類中的靜態(tài)同步方法而無(wú)需等待。不管類中的那個(gè)靜態(tài)同步方法是否被調(diào)用,一個(gè)類只能由一個(gè)線程同時(shí)執(zhí)行。

public static synchronized void add(int value){
    count += value;
}
實(shí)例方法中的同步塊

有時(shí)你不需要同步整個(gè)方法,而是同步方法中的一部分。

public void add(int value){
    synchronized(this){
       this.count += value;
    }
}

示例使用Java同步塊構(gòu)造器來(lái)標(biāo)記一塊代碼是同步的。該代碼在執(zhí)行時(shí)和同步方法一樣。在上例中,使用了“this”,即為調(diào)用add方法的實(shí)例本身。在同步構(gòu)造器中用括號(hào)括起來(lái)的對(duì)象叫做監(jiān)視器對(duì)象。

靜態(tài)方法中的同步塊

和上面類似,下面是兩個(gè)靜態(tài)方法同步的例子。這些方法同步在該方法所屬的類對(duì)象上。

public class MyClass {
    public static synchronized void log1(String msg1, String msg2){
       log.writeln(msg1);
       log.writeln(msg2);
    }

    public static void log2(String msg1, String msg2){
       synchronized(MyClass.class){
          log.writeln(msg1);
          log.writeln(msg2);
       }
    }
  }

這兩個(gè)方法不允許同時(shí)被線程訪問(wèn)。
如果第二個(gè)同步塊不是同步在MyClass.class這個(gè)對(duì)象上。那么這兩個(gè)方法可以同時(shí)被線程訪問(wèn)。

6、線程通信

線程通信的目標(biāo)是使線程間能夠互相發(fā)送信號(hào)。另一方面,線程通信使線程能夠等待其他線程的信號(hào)。

通過(guò)共享對(duì)象通信

線程間發(fā)送信號(hào)的一個(gè)簡(jiǎn)單方式是在共享對(duì)象的變量里設(shè)置信號(hào)值。

public class MySignal{
  protected boolean hasDataToProcess = false;

  public synchronized boolean hasDataToProcess(){
    return this.hasDataToProcess;
  }

  public synchronized void setHasDataToProcess(boolean hasData){
    this.hasDataToProcess = hasData;
  }
}

線程A在一個(gè)同步塊里設(shè)置boolean型成員變量hasDataToProcess為true,線程B也在同步塊里讀取hasDataToProcess這個(gè)成員變量。
線程A和B必須獲得指向一個(gè)MySignal共享實(shí)例的引用,以便進(jìn)行通信。如果它們持有的引用指向不同的MySingal實(shí)例,那么彼此將不能檢測(cè)到對(duì)方的信號(hào)。

忙等待(Busy Wait)

線程B運(yùn)行在一個(gè)循環(huán)里,等待線程A的一個(gè)可執(zhí)行的信號(hào)。

protected MySignal sharedSignal = ...

...
while(!sharedSignal.hasDataToProcess()){
   //do nothing... busy waiting
}
wait(),notify()和notifyAll()

除非忙等待的時(shí)間特別短,否則會(huì)浪費(fèi)CPU資源。合理的做法:讓等待線程進(jìn)入睡眠或者非運(yùn)行狀態(tài),直到它接收到它等待的信號(hào)。

java.lang.Object 類定義了三個(gè)方法,wait()、notify()和notifyAll()來(lái)實(shí)現(xiàn)這個(gè)等待機(jī)制。

一個(gè)線程一旦調(diào)用了任意對(duì)象的wait()方法,就會(huì)變?yōu)榉沁\(yùn)行狀態(tài),直到另一個(gè)線程調(diào)用了同一個(gè)對(duì)象的notify()方法。

為了調(diào)用wait()或者notify(),線程必須先獲得那個(gè)對(duì)象的鎖。也就是說(shuō),線程必須在同步塊里調(diào)用wait()或者notify()。

在wait()/notify()機(jī)制中,不要使用全局對(duì)象,字符串常量等。應(yīng)該使用對(duì)應(yīng)唯一的對(duì)象

public class MonitorObject{
}

public class MyWaitNotify{

  MonitorObject myMonitorObject = new MonitorObject();

  public void doWait(){
    synchronized(myMonitorObject){
      try{
        myMonitorObject.wait();
      } catch(InterruptedException e){...}
    }
  }

  public void doNotify(){
    synchronized(myMonitorObject){
      myMonitorObject.notify();
    }
  }
}

不管是等待線程還是喚醒線程都在同步塊里調(diào)用wait()和notify()。這是強(qiáng)制性的!一個(gè)線程如果沒(méi)有持有對(duì)象鎖,將不能調(diào)用wait(),notify()或者notifyAll()。否則,會(huì)拋出IllegalMonitorStateException異常。

一旦線程調(diào)用了wait()方法,它就釋放了所持有的監(jiān)視器對(duì)象上的鎖。這將允許其他線程也可以調(diào)用wait()或者notify()。

被喚醒的線程必須重新獲得監(jiān)視器對(duì)象的鎖,才可以退出wait()的方法調(diào)用,因?yàn)閣ait方法調(diào)用運(yùn)行在同步塊里面。如果多個(gè)線程被notifyAll()喚醒,那么在同一時(shí)刻將只有一個(gè)線程可以退出wait()方法,因?yàn)槊總€(gè)線程在退出wait()前必須獲得監(jiān)視器對(duì)象的鎖。

丟失信號(hào)

notify()和notifyAll()方法不會(huì)保存調(diào)用它們的方法,如果方法被調(diào)用時(shí),沒(méi)有線程處于等待狀態(tài)。通知信號(hào)過(guò)后便丟棄了。因此,如果一個(gè)線程先于被通知線程調(diào)用wait()前調(diào)用了notify(),等待的線程將錯(cuò)過(guò)這個(gè)信號(hào)。在某些情況下,這可能使線程錯(cuò)過(guò)了喚醒信號(hào),永遠(yuǎn)在等待不再醒來(lái)。

為了避免丟失信號(hào),必須把它們保存在信號(hào)類里。在MyWaitNotify的例子中,通知信號(hào)應(yīng)被存儲(chǔ)在MyWaitNotify實(shí)例的一個(gè)成員變量里。

public class MyWaitNotify2{

  MonitorObject myMonitorObject = new MonitorObject();
  boolean wasSignalled = false;

  public void doWait(){
    synchronized(myMonitorObject){
      if(!wasSignalled){
        try{
          myMonitorObject.wait();
         } catch(InterruptedException e){...}
      }
      //clear signal and continue running.
      wasSignalled = false;
    }
  }

  public void doNotify(){
    synchronized(myMonitorObject){
      wasSignalled = true;
      myMonitorObject.notify();
    }
  }
}

在上述例子中,doNotify()方法在調(diào)用notify()前把wasSignalled變量設(shè)為true。同時(shí),留意doWait()方法在調(diào)用wait()前會(huì)檢查wasSignalled變量。

為了避免信號(hào)丟失,用一個(gè)變量來(lái)保存是否被通知過(guò)。在notify前,設(shè)置自己已經(jīng)被通知過(guò)。在wait后,設(shè)置自己沒(méi)有被通知過(guò),需要等待通知。。

假喚醒

線程有可能在沒(méi)有調(diào)用過(guò)notify()和notifyAll()的情況下醒來(lái)。這就是所謂的假喚醒(spurious wakeups)。等待線程即使沒(méi)有收到正確的信號(hào),也能夠執(zhí)行后續(xù)的操作。

為了防止假喚醒,保存信號(hào)的成員變量將在一個(gè)while循環(huán)里接受檢查,而不是在if表達(dá)式里。這樣的一個(gè)while循環(huán)叫做自旋鎖。

public class MyWaitNotify3{

  MonitorObject myMonitorObject = new MonitorObject();
  boolean wasSignalled = false;

  public void doWait(){
    synchronized(myMonitorObject){
      while(!wasSignalled){
        try{
          myMonitorObject.wait();
         } catch(InterruptedException e){...}
      }
      //clear signal and continue running.
      wasSignalled = false;
    }
  }

  public void doNotify(){
    synchronized(myMonitorObject){
      wasSignalled = true;
      myMonitorObject.notify();
    }
  }
}

如果等待線程沒(méi)有收到信號(hào)就喚醒,wasSignalled變量將變?yōu)閒alse,while循環(huán)會(huì)再執(zhí)行一次,促使醒來(lái)的線程回到等待狀態(tài)。

目前的JVM實(shí)現(xiàn)自旋會(huì)消耗CPU,如果長(zhǎng)時(shí)間不調(diào)用doNotify方法,doWait方法會(huì)一直自旋,CPU會(huì)消耗太大。

7、TheadLocal

ThreadLocal類創(chuàng)建的變量只被同一個(gè)線程進(jìn)行讀和寫操作。因此,盡管有兩個(gè)線程同時(shí)執(zhí)行一段相同的代碼,而且這段代碼又有一個(gè)指向同一個(gè)ThreadLocal變量的引用,但是這兩個(gè)線程依然不能看到彼此的ThreadLocal變量域。

//創(chuàng)建一個(gè)ThreadLocal變量:每個(gè)線程僅需要實(shí)例化一次即可。
//每個(gè)線程只能看到私有的ThreadLocal實(shí)例,不同的線程在給ThreadLocal對(duì)象設(shè)置不同的值,也不能看到彼此的修改。
private ThreadLocal myThreadLocal = new ThreadLocal();

//設(shè)置、獲取數(shù)據(jù)
myThreadLocal.set("A thread local value");
String threadLocalValue = (String) myThreadLocal.get();

//創(chuàng)建泛型對(duì)象
private ThreadLocal myThreadLocal1 = new ThreadLocal();

myThreadLocal1.set("Hello ThreadLocal");
String threadLocalValues = myThreadLocal.get();

InheritableThreadLocal類是ThreadLocal的子類。為了解決ThreadLocal實(shí)例內(nèi)部每個(gè)線程都只能看到自己的私有值,所以InheritableThreadLocal允許一個(gè)線程創(chuàng)建的所有子線程訪問(wèn)其父線程的值。

引用

1、并發(fā)編程網(wǎng)-Java并發(fā)性和多線程
2、蘭亭風(fēng)雨專欄

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/66891.html

相關(guān)文章

  • Java問(wèn)題匯總,持續(xù)更新到GitHub

    摘要:目錄介紹問(wèn)題匯總具體問(wèn)題好消息博客筆記大匯總年月到至今,包括基礎(chǔ)及深入知識(shí)點(diǎn),技術(shù)博客,學(xué)習(xí)筆記等等,還包括平時(shí)開(kāi)發(fā)中遇到的匯總,當(dāng)然也在工作之余收集了大量的面試題,長(zhǎng)期更新維護(hù)并且修正,持續(xù)完善開(kāi)源的文件是格式的同時(shí)也開(kāi)源了生活博客,從年 目錄介紹 00.Java問(wèn)題匯總 01.具體問(wèn)題 好消息 博客筆記大匯總【16年3月到至今】,包括Java基礎(chǔ)及深入知識(shí)點(diǎn),Android技...

    beita 評(píng)論0 收藏0
  • java&javaweb學(xué)習(xí)筆記(匯總)

    摘要:我的學(xué)習(xí)筆記匯總標(biāo)簽筆記分為兩大部分和筆記內(nèi)容主要是對(duì)一些基礎(chǔ)特性和編程細(xì)節(jié)進(jìn)行總結(jié)整理,適合了解基礎(chǔ)語(yǔ)法,想進(jìn)一步深入學(xué)習(xí)的人如果覺(jué)得不錯(cuò),請(qǐng)給,這也是對(duì)我的鼓勵(lì),有什么意見(jiàn)歡迎留言反饋目錄基礎(chǔ)鞏固筆記反射基礎(chǔ)鞏固筆記泛型基礎(chǔ)鞏 我的java&javaweb學(xué)習(xí)筆記(匯總) 標(biāo)簽: java [TOC] 筆記分為兩大部分:javase和javaweb javase javawe...

    yagami 評(píng)論0 收藏0
  • int和Integer深入分析

    摘要:對(duì)象頭的另外一部分是類型指針,即對(duì)象指向它的類元數(shù)據(jù)的指針,虛擬機(jī)通過(guò)這個(gè)指針來(lái)確定這個(gè)對(duì)象是哪個(gè)類的實(shí)例。并不是所有的虛擬機(jī)實(shí)現(xiàn)都必須在對(duì)象數(shù)據(jù)上保留類型指針,換句話說(shuō),查找對(duì)象的元數(shù)據(jù)信息并不一定要經(jīng)過(guò)對(duì)象本身,這點(diǎn)將在節(jié)討論。 目錄介紹 1.關(guān)于int和Integer的問(wèn)題區(qū)別分析 2.Integer的值緩存的原理 2.1 Java 5 中引入緩存特性 2.2 Intege...

    Half 評(píng)論0 收藏0
  • Java面試 32個(gè)核心必考點(diǎn)完全解析

    摘要:如問(wèn)到是否使用某框架,實(shí)際是是問(wèn)該框架的使用場(chǎng)景,有什么特點(diǎn),和同類可框架對(duì)比一系列的問(wèn)題。這兩個(gè)方向的區(qū)分點(diǎn)在于工作方向的側(cè)重點(diǎn)不同。 [TOC] 這是一份來(lái)自嗶哩嗶哩的Java面試Java面試 32個(gè)核心必考點(diǎn)完全解析(完) 課程預(yù)習(xí) 1.1 課程內(nèi)容分為三個(gè)模塊 基礎(chǔ)模塊: 技術(shù)崗位與面試 計(jì)算機(jī)基礎(chǔ) JVM原理 多線程 設(shè)計(jì)模式 數(shù)據(jù)結(jié)構(gòu)與算法 應(yīng)用模塊: 常用工具集 ...

    JiaXinYi 評(píng)論0 收藏0
  • java 8 實(shí)戰(zhàn)》讀書(shū)筆記 -第六章 用流收集數(shù)據(jù)

    摘要:分區(qū)函數(shù)返回一個(gè)布爾值,這意味著得到的分組的鍵類型是,于是它最多可以分為兩組是一組,是一組。當(dāng)遍歷到流中第個(gè)元素時(shí),這個(gè)函數(shù)執(zhí)行時(shí)會(huì)有兩個(gè)參數(shù)保存歸約結(jié)果的累加器已收集了流中的前個(gè)項(xiàng)目,還有第個(gè)元素本身。 一、收集器簡(jiǎn)介 把列表中的交易按貨幣分組: Map transactionsByCurrencies = transactions.stream().collect(groupi...

    Airy 評(píng)論0 收藏0
  • Jmetter并發(fā)測(cè)試工具使用筆記

    摘要:前言是一個(gè)開(kāi)源的壓力測(cè)試工具,常用于應(yīng)用壓力測(cè)試,本文針使用其對(duì)接口進(jìn)行并發(fā)性能測(cè)試,做筆記以備忘。 前言 Jmetter是一個(gè)開(kāi)源的壓力測(cè)試工具,常用于Web應(yīng)用壓力測(cè)試,本文針使用其對(duì)api接口進(jìn)行并發(fā)性能測(cè)試,做筆記以備忘。 目錄 一、下載和安裝 1. Jmetter下載 2.下載并安...

    Jason_Geng 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

Lsnsh

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<