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

理解Java多線(xiàn)程之并發(fā)編程

 更新時(shí)間:2023年02月02日 08:34:24   作者:Seattle小小瓜  
這篇文章主要介紹了理解Java多線(xiàn)程之并發(fā)編程的相關(guān)資料,需要的朋友可以參考下

今天深度學(xué)習(xí)一下《Java并發(fā)編程的藝術(shù)》的第1章并發(fā)編程的挑戰(zhàn),深入理解Java多線(xiàn)程,看看多線(xiàn)程中的坑。

注意,哈肯的程序員讀書(shū)筆記并不是抄書(shū),而是將書(shū)中精華內(nèi)容和哈肯的開(kāi)發(fā)經(jīng)驗(yàn)相結(jié)合,給你系統(tǒng)地講述我對(duì)相關(guān)知識(shí)理解。如果你發(fā)現(xiàn)哈肯講的內(nèi)容跟書(shū)中內(nèi)容差異很大也不足為奇。

1 多線(xiàn)程的使用場(chǎng)景

在實(shí)際的商業(yè)系統(tǒng)中,為了提升程序的性能,我們經(jīng)常會(huì)使用到多線(xiàn)程。java多線(xiàn)程也是后臺(tái)開(kāi)發(fā)崗、Android開(kāi)發(fā)崗招聘面試和筆試時(shí)的熱門(mén)問(wèn)題。至少,到目前為止我面試過(guò)的200多位開(kāi)發(fā)同學(xué)時(shí),以及我曾經(jīng)的幾次求職被面試時(shí),多半都會(huì)問(wèn)java多線(xiàn)程并發(fā)相關(guān)的問(wèn)題。

2 多線(xiàn)程的缺點(diǎn)

多線(xiàn)程能充分利用多核CPU的特性,但并不意味著多線(xiàn)程就一定比單線(xiàn)程更快,并發(fā)編程也存在許多限制和挑戰(zhàn),例如多線(xiàn)程間的上下文切換會(huì)有開(kāi)銷(xiāo)、多線(xiàn)程中的數(shù)據(jù)一致性問(wèn)題、線(xiàn)程死鎖問(wèn)題、系統(tǒng)資源限制。

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

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

CPU使用時(shí)間片算法,將處理時(shí)間輪著分配給不同的線(xiàn)程,所以即使是單核CPU也支持多線(xiàn)程,這個(gè)時(shí)間片非常短,一般是幾十毫秒(ms),CPU不停的切換線(xiàn)程執(zhí)行,讓我們感覺(jué)多個(gè)線(xiàn)程是同時(shí)執(zhí)行的。CPU在切換線(xiàn)程前會(huì)保存上一個(gè)任務(wù)的狀態(tài),以便下次切換回這個(gè)線(xiàn)程時(shí),可以正常繼續(xù)執(zhí)行。我們把線(xiàn)程的狀態(tài)保存到再加載的過(guò)程稱(chēng)為一次上下文切換。

《Java并發(fā)編程的藝術(shù)》作者寫(xiě)了一段程序,讓2個(gè)線(xiàn)程同時(shí)不斷地做變量自增操作,結(jié)果證明用2個(gè)線(xiàn)程并行甚至比單線(xiàn)程串行更慢一些。通過(guò)使用Lmbench3測(cè)量上下文切換的時(shí)長(zhǎng),發(fā)現(xiàn)上述多線(xiàn)程代碼中每秒高達(dá)1000多次的線(xiàn)程上下文切換。因此不是多線(xiàn)程就一定更快,還要看在線(xiàn)程中干了什么。

(2)如何減少上下文切換

減少上下文切換的方法有:無(wú)鎖并發(fā)編程、CAS算法、減少不必要的線(xiàn)程、使用協(xié)程。

  • 無(wú)鎖并發(fā)編程。多線(xiàn)程競(jìng)爭(zhēng)鎖時(shí),會(huì)引起上下文切換,通過(guò)避免使用鎖,可以減少線(xiàn)程的切換,例如將數(shù)據(jù)用ID分段,不同的線(xiàn)程處理不同段的數(shù)據(jù)。
  • CAS算法。Java的Atomic包使用CAS算法來(lái)更新數(shù)據(jù),而不需要加鎖。
  • 減少不必要的線(xiàn)程。當(dāng)任務(wù)很少時(shí),盡量減少不必要的線(xiàn)程,避免造成大量線(xiàn)程都處于等待狀態(tài)。
  • 協(xié)程:在單線(xiàn)程里實(shí)現(xiàn)多任務(wù)的調(diào)度,并在單線(xiàn)程里維持多個(gè)任務(wù)間的切換。

2.2 多線(xiàn)程中的數(shù)據(jù)一致性問(wèn)題

(1)線(xiàn)程中訪問(wèn)外部數(shù)據(jù)的過(guò)程

每個(gè)線(xiàn)程都有自己的棧,保存線(xiàn)程中創(chuàng)建的局部變量,如果線(xiàn)程中使用到外部的變量,則線(xiàn)程通常會(huì)把改外部變量復(fù)制一份到線(xiàn)程棧中,當(dāng)修改完后,再將數(shù)據(jù)同步回外部。

(2)線(xiàn)程內(nèi)操作的原子性問(wèn)題

一個(gè)操作會(huì)由多個(gè)cpu指令構(gòu)造。例如,創(chuàng)建對(duì)象操作大致分為幾步:為對(duì)象分配內(nèi)存、成員變量的值初始化、調(diào)用構(gòu)造方法、返回對(duì)象的引用;又例如,線(xiàn)程中對(duì)一個(gè)外部變量的賦值(修改)操作大致分為幾步:在當(dāng)前線(xiàn)程棧創(chuàng)建變量副本,修改變量值,將變量值的修改同步回外部,由于CPU的時(shí)間片機(jī)制,每個(gè)線(xiàn)程獲得時(shí)間片后,能執(zhí)行的指令數(shù)量是有限的,可能一個(gè)操作還未完成,而時(shí)間片到了,需要保存上下文并切換到下一個(gè)線(xiàn)程。因此,無(wú)法保證操作的原子性。

(3)共享數(shù)據(jù)的可見(jiàn)性問(wèn)題

觀察上述的原子性問(wèn)題中的例子,線(xiàn)程中對(duì)一個(gè)外部變量的賦值,可能線(xiàn)程A中剛創(chuàng)建了外部變量的副本,而線(xiàn)程B已經(jīng)對(duì)該外部變量進(jìn)行了修改,但線(xiàn)程A中是不知道的。即,一個(gè)線(xiàn)程對(duì)共享數(shù)據(jù)的修改,不能立刻被其他線(xiàn)程所看見(jiàn),這就引起了數(shù)據(jù)一致性的問(wèn)題。

(4)有序性問(wèn)題

CPU單個(gè)核中,包含多個(gè)ALU單元(Arithmetic Logic Unit,即算術(shù)邏輯單元),用來(lái)執(zhí)行算數(shù)運(yùn)算和邏輯運(yùn)算。目前Intel的Haswell架構(gòu)的CPU(第四代酷睿處理器開(kāi)始)有4個(gè)ALU單元。所以單核CPU在同一時(shí)刻是同時(shí)執(zhí)行多個(gè)指令的。CPU會(huì)把多條指令進(jìn)行重排序,把沒(méi)有依賴(lài)關(guān)系的指令同時(shí)放到各ALU中執(zhí)行。例如3個(gè)操作按如下順序?qū)懘a a=1; b=2; c=a+b; 由于a=1和b=2不存在依賴(lài)關(guān)系,2個(gè)指令可能會(huì)被重排序,而c=a+b存在依賴(lài)關(guān)系,這個(gè)指令一定會(huì)在前2個(gè)指令執(zhí)行完后再執(zhí)行,最終一定是c=3。請(qǐng)看如下代碼:

public class VolatileTest {
    private int a = 1;
    private boolean status = false;

    public void setStatus() {
        a = 2;
        status = true;
    }
 
    public void test() {
        if (status) {
            int b = a + 1;
            System.out.print(b);
        }
    }
}

(5)如何解決多線(xiàn)程的數(shù)據(jù)一致性問(wèn)題

由于以上4點(diǎn),引發(fā)了多線(xiàn)程中數(shù)據(jù)的一致性問(wèn)題。解決辦法主要有2個(gè):

  • 使用volatile關(guān)鍵字。相對(duì)synchronized來(lái)說(shuō),volatile是一種輕量級(jí)的同步機(jī)制,有2個(gè)作用:一是保證共享變量對(duì)所有線(xiàn)程的可見(jiàn)性,即本地副本修改后會(huì)強(qiáng)制刷新回外部;二是禁止指令重排序優(yōu)化。但要注意volatile只對(duì)單個(gè)變量讀/寫(xiě)操作具有原子性,對(duì)i++這種復(fù)合操作(取值、加1、賦值)是無(wú)法保證其原子性的。要保證多線(xiàn)程中i++這種復(fù)合操作的原子性,可以改用CAS的實(shí)現(xiàn)類(lèi),例如用AtomicInteger代替int。
  • 加鎖。通過(guò)鎖來(lái)實(shí)現(xiàn)同一時(shí)間只有1個(gè)線(xiàn)程可以訪問(wèn)共享變量。

2.3 線(xiàn)程死鎖問(wèn)題

死鎖產(chǎn)生需要同時(shí)滿(mǎn)足4個(gè)條件:

  • 資源的互斥使用,即當(dāng)資源被一個(gè)線(xiàn)程占有時(shí),別的線(xiàn)程不能用。
  • 不可搶占,即資源請(qǐng)的求者不能從使用者手上強(qiáng)制奪取資源,只能等待對(duì)方釋放。
  • 請(qǐng)求和保持,即在請(qǐng)求其他資源的同時(shí)還保持對(duì)現(xiàn)有資源的占有。
  • 循環(huán)等待,例如線(xiàn)程1持有資源A,請(qǐng)求資源B,而線(xiàn)程2持有資源B,請(qǐng)求資源A。

下面的代碼演示對(duì)資源的請(qǐng)求和保持,持有res1不釋放,同時(shí)對(duì)res2進(jìn)行請(qǐng)求:

synchronized (res1) {
   ... // 執(zhí)行一些操作 
   synchronized (res2) {
      ...
   }
}

解決死鎖的方法

只要讓產(chǎn)生死鎖的4個(gè)條件中任何一個(gè)不成立,就可以避免死鎖。具體做法通常有:

  • 使用Lock接口的tryLock()。
  • 避免一個(gè)線(xiàn)程中同時(shí)獲得多個(gè)鎖。例如上面代碼所示,同時(shí)獲得res1和res2的鎖才能完成執(zhí)行。
  • 避免一個(gè)鎖中占用多個(gè)資源,即要縮小鎖的范圍。

Lock接口包含4個(gè)給對(duì)象加鎖的方法,如下所示:

public interface Lock {
     /**阻塞,直到另外一個(gè)線(xiàn)程釋放鎖*/
    void lock();
    
    /**以可被中斷的方式獲取鎖。調(diào)用正在等待獲得鎖的線(xiàn)程的interrupt()方法可中斷*/
    void lockInterruptibly() throws InterruptedException;
    
    /**非阻塞,嘗試的獲取鎖,如果獲取到則返回true,否則返回false*/
    boolean tryLock();
    /**阻塞,試圖獲取鎖,最多阻塞等待指定時(shí)間,如果獲取到則返回true,否則返回false*/
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    
    void unlock();
    ...
}
Lock lock = new ReentrantLock();
if(lock.tryLock()) {
    //成功獲得鎖
    try{
        ...
    } catch(Exception ex){
     
    } finally{
         lock.unlock();   //釋放鎖
    } 
} else {
    //不能獲取鎖
    ...
}
class Account {
    private int money;
    synchronized boolean transfer(Account target, int money){
        if (this.money > money) {
            this.money -= money;
            target.money += money;
            return true;
        }
        return false;
    } 
}

深入理解Java多線(xiàn)程(1) - Java并發(fā)編程的藝術(shù)_并發(fā)編程

class Account {
    private int money;
    boolean transfer(Account target, int money){
        synchronized(Account.class) {
            if (this.money > money) {
              this.money -= money;
              target.money += money;
              return true;
            }
        }
        return false;
    } 
}

2.4 系統(tǒng)資源限制

系統(tǒng)的資源總是有限的,無(wú)節(jié)制地創(chuàng)建大量的線(xiàn)程,只會(huì)造成大量的線(xiàn)程上下文切換的開(kāi)銷(xiāo),并不能實(shí)際提高程序的效率。按照大家的常規(guī)經(jīng)驗(yàn),如果是IO密集型,則線(xiàn)程池的核心線(xiàn)程數(shù)宜為2N+1;如果是
CPU密集型,則線(xiàn)程池核心線(xiàn)程數(shù)宜為N+1;其中N為CPU核數(shù)。

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

相關(guān)文章

最新評(píng)論

石渠县| 兴业县| 北碚区| 新丰县| 隆子县| 永康市| 烟台市| 琼海市| 新余市| 偏关县| 莱阳市| 都兰县| 广汉市| 武冈市| 阿拉尔市| 鹿邑县| 多伦县| 西丰县| 汉阴县| 台北县| 扬州市| 青神县| 江阴市| 枣阳市| 石屏县| 澜沧| 鄯善县| 容城县| 那坡县| 康马县| 乐至县| 南宁市| 武强县| 清镇市| 芦山县| 萝北县| 平武县| 临泉县| 黑山县| 平江县| 梁河县|