成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

Java 并發(fā)編程

nihao / 1375人閱讀

摘要:并發(fā)編程的核心是為了提高電腦資源的利用率,因?yàn)楝F(xiàn)代操作系統(tǒng)都是多核的,可以同時(shí)跑多個(gè)線程。合理配置線程池,密集型任務(wù)配置少數(shù)線程池如個(gè)數(shù),密集型任務(wù)配置多一點(diǎn)的線程池如個(gè)數(shù),其次是使用有界隊(duì)列即使發(fā)現(xiàn)錯(cuò)誤。

并發(fā)編程的核心是為了提高電腦資源的利用率,因?yàn)楝F(xiàn)代操作系統(tǒng)都是多核的,可以同時(shí)跑多個(gè)線程。那么是不是線程越多越好? 由于線程的切換涉及上下文的切換,所謂上下文就是線程運(yùn)行時(shí)需要的資源,系統(tǒng)要分配給它消耗時(shí)間。所以為了減少上下文的切換,我們有以下幾種方法:

CAS算法

協(xié)程,單線程里實(shí)現(xiàn)多任務(wù)調(diào)度

避免創(chuàng)建不需要的線程因此

協(xié)程和線程區(qū)別:每個(gè)線程OS會(huì)給它分配固定大小的內(nèi)存(一般2MB)來存儲(chǔ)當(dāng)前調(diào)用或掛起的函數(shù)的內(nèi)部變量,固定大小的棧意味著內(nèi)存利用率很低或有時(shí)面對復(fù)雜函數(shù)無法滿足要求,協(xié)成就實(shí)現(xiàn)了可動(dòng)態(tài)伸縮的棧(最小2KB,最大1GB).其二OS線程受操作系統(tǒng)調(diào)度,調(diào)度時(shí)要將當(dāng)前線程狀態(tài)存到內(nèi)存,將另一個(gè)線程執(zhí)行指令放到寄存器,這幾步很耗時(shí)。Go調(diào)度器并非硬件調(diào)度器,而是Go語言內(nèi)置的一中機(jī)制,因此goroutine調(diào)度時(shí)則不需要切換上下文。

Java并發(fā)機(jī)制的底層實(shí)現(xiàn)原理,java代碼編譯成字節(jié)碼后加載到JVM中,JVM執(zhí)行字節(jié)碼最終轉(zhuǎn)化成匯編命令在CPU上運(yùn)行,因此Java所使用的并發(fā)機(jī)制依賴JVM的實(shí)現(xiàn)和CPU指令。Java大部分并發(fā)容器和框架都依賴于volatile和原子操作的實(shí)現(xiàn)原理。

volatile:被volatile修身的變量在進(jìn)行寫操作時(shí)會(huì)多出一行以Lock為前綴的匯編代碼,Lock前綴的指令在多核處理器下執(zhí)行兩件事情,1.將當(dāng)前處理器緩存行(緩存可分配的最小單元)的數(shù)據(jù)寫入到系統(tǒng)內(nèi)2.寫回內(nèi)存的操作使其它處理器地址為該緩存的內(nèi)存無效。這兩條保證了所謂的可見性

原子操作的實(shí)現(xiàn):首先看一看處理器是如何實(shí)現(xiàn)原子操作的,有兩核CPU1和CPU2,兩個(gè)處理器同時(shí)對數(shù)據(jù)i進(jìn)行操作,CPU采取總線鎖使得一個(gè)數(shù)據(jù)不能同時(shí)被多個(gè)處理器操作。大概原理就是使用處理器提供的一個(gè)LOCK信號(hào),一個(gè)處理器在總線上輸出此信號(hào)時(shí)另一個(gè)處理器的請求被阻塞住。這樣會(huì)導(dǎo)致別的處理器不能處理其它內(nèi)存地址的數(shù)據(jù),因?yàn)榭偩€鎖開銷比較大出現(xiàn)了緩存鎖,使得CPU1修改緩存行1中數(shù)據(jù)時(shí)若使用了緩存鎖定,那么CPU2就不能再緩存該緩存。處理器提供了一系列命令支持這兩種機(jī)制,如BTS,XADD等,被這些指令操作的內(nèi)存區(qū)域就會(huì)加鎖,使其它處理器不能同時(shí)訪問。

Java內(nèi)存模型

Java之間通過共享內(nèi)存進(jìn)行通信,處理器和編譯器為了提高性能會(huì)對指令進(jìn)行重排序,這在單線程情況下不會(huì)發(fā)生異常,但是在多線程下就會(huì)造成結(jié)果的不一致

int a=0;
public int calculate(){
    a=1;  1 
    boolean flag=true;  2
    if(flag){ 
        return a*a;
    }
    return 0;
}

現(xiàn)有兩個(gè)線程執(zhí)行這段代碼,線程A執(zhí)行時(shí)對指令進(jìn)行了重排序先制行 2 在執(zhí)行 1,在中間線程B插入了進(jìn)來此時(shí)a=1值還沒被寫入導(dǎo)致返回結(jié)果為0發(fā)生錯(cuò)誤。

處理器遵循as-if-serial語義,即不管如何重排序結(jié)果不變,但是多線程情況下會(huì)出現(xiàn)錯(cuò)誤

為了避免重排序,Java引入了volatile變量,使得語句在操作被volatile修飾的變量時(shí)禁止指令重排序。在執(zhí)行指令時(shí)插入內(nèi)存屏障也就是這個(gè)目的,最關(guān)鍵的是volatile的讀/寫內(nèi)存語義如下

寫語義:寫一個(gè)volatile變量時(shí)會(huì)把線程對應(yīng)本地內(nèi)存的值刷新到主存中

讀語義:讀一個(gè)volatile變量時(shí)會(huì)把本地內(nèi)存的值設(shè)置為無效,從主存中讀

volatile的缺陷在于這個(gè)動(dòng)作是不完全的,因此又提出了CAS機(jī)制,CAS會(huì)使用處理器提供的機(jī)器級(jí)別的原子命令(CMPXCHG),原子執(zhí)行讀-改-寫操作。Java concurrent包中一個(gè)通用化的實(shí)現(xiàn)模式就是結(jié)合兩者,步驟如下

聲明共享變量為volatile

使用CAS實(shí)現(xiàn)線程間的同步和通信,(自旋樂觀鎖,性能大大提升)

Java線程池

線程池的核心作用就是維護(hù)固定的幾個(gè)線程,有任務(wù)來的時(shí)候直接使用避免創(chuàng)建/銷毀線程導(dǎo)致的額外開銷。 線程池執(zhí)行流程如下:

提交任務(wù)-->核心線程池已滿? 是 提交任務(wù)到消息隊(duì)列--->隊(duì)列已滿? 是 按指定策略執(zhí)行
                          否 創(chuàng)建線程執(zhí)行任務(wù)                否 加進(jìn)隊(duì)列

了解了線程池的原理最重要的就是如何是去使用它,而使用的關(guān)鍵就是參數(shù)的設(shè)置。

       public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
        

以上是ThreadPoolExecutor的構(gòu)造函數(shù),我們逐一看一看各參數(shù)的含義

corePoolSize 一直維護(hù)的線程數(shù)

maximumPoolSize 最大線程數(shù)

keepAliveTime 多余線程存活的時(shí)間(實(shí)際線程數(shù)比corePool多的那部分)

workQueue 存儲(chǔ)線程的隊(duì)列,可選擇ArrayBlockingQueue等

threadFactory 創(chuàng)建線程時(shí)的用到的工廠,可通過自定義工廠創(chuàng)建更有意義的線程名稱

handler 隊(duì)列滿時(shí)采取的策略 有AbortPolicy(直接拋出異常)/CallerRunsPolicy(只用調(diào)用者所在的線程執(zhí)行)等等

提交線程池有兩個(gè)方法,一個(gè)是submit這個(gè)不需要返回值,一個(gè)是submit會(huì)返回一個(gè)future對象,并通過future的get()方法獲取返回值(該方法會(huì)阻塞直到線程完成任務(wù))。

合理配置線程池,CPU密集型任務(wù)配置少數(shù)線程池如N(CPU個(gè)數(shù))+1,I/O密集型任務(wù)配置多一點(diǎn)的線程池如2N(CPU個(gè)數(shù)),其次是使用有界隊(duì)列即使發(fā)現(xiàn)錯(cuò)誤。

Executor框架

在HotSpot VM的線程模型中,Java線程被一對一的映射成本地操作系統(tǒng)的線程,操作系統(tǒng)會(huì)調(diào)度線程把它們分配給可用的CPU。在上層Java通過用戶級(jí)調(diào)度器Executor將任務(wù)映射為幾個(gè)線程,在下層操作系統(tǒng)內(nèi)核將這些線程映射到硬件處理器上面。

Executor的出現(xiàn)將任務(wù)與如何執(zhí)行任務(wù)分離開了,避免了每創(chuàng)建一個(gè)線程就要執(zhí)行它。Executor的整個(gè)架構(gòu)有一下幾個(gè)要點(diǎn)

實(shí)現(xiàn)了Runnable和Callable的對象可提交到Executor運(yùn)行

可返回Future獲取線程執(zhí)行后的返回值

內(nèi)部維護(hù)一個(gè)線程池(上面介紹的)來處理提交過來的任務(wù)

Executor最核心的就是ThreadPoolExecutor,下面介紹以下以及各自使用場景

FixedThreadPool 固定線程個(gè)數(shù),用于高負(fù)載的服務(wù)器,滿足資源的管理需求

SingleThreadPool 單個(gè)線程,保證順序的執(zhí)行任務(wù)

CachedThreadPool 大小無界的線程池,使用負(fù)載比較輕的服務(wù)器

ScheduledThreadPoolExecutor 后臺(tái)周期執(zhí)行任務(wù)

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

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/68960.html

相關(guān)文章

  • Java多線程學(xué)習(xí)(七)并發(fā)編程中一些問題

    摘要:相比與其他操作系統(tǒng)包括其他類系統(tǒng)有很多的優(yōu)點(diǎn),其中有一項(xiàng)就是,其上下文切換和模式切換的時(shí)間消耗非常少。因?yàn)槎嗑€程競爭鎖時(shí)會(huì)引起上下文切換。減少線程的使用。很多編程語言中都有協(xié)程。所以如何避免死鎖的產(chǎn)生,在我們使用并發(fā)編程時(shí)至關(guān)重要。 系列文章傳送門: Java多線程學(xué)習(xí)(一)Java多線程入門 Java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(1) java多線程學(xué)習(xí)(二)syn...

    dingding199389 評(píng)論0 收藏0
  • Java多線程學(xué)習(xí)(七)并發(fā)編程中一些問題

    摘要:因?yàn)槎嗑€程競爭鎖時(shí)會(huì)引起上下文切換。減少線程的使用。舉個(gè)例子如果說服務(wù)器的帶寬只有,某個(gè)資源的下載速度是,系統(tǒng)啟動(dòng)個(gè)線程下載該資源并不會(huì)導(dǎo)致下載速度編程,所以在并發(fā)編程時(shí),需要考慮這些資源的限制。 最近私下做一項(xiàng)目,一bug幾日未解決,總惶恐。一日頓悟,bug不可怕,怕的是項(xiàng)目不存在bug,與其懼怕,何不與其剛正面。 系列文章傳送門: Java多線程學(xué)習(xí)(一)Java多線程入門 Jav...

    yimo 評(píng)論0 收藏0
  • 并發(fā)編程 - 探索一

    摘要:并發(fā)表示在一段時(shí)間內(nèi)有多個(gè)動(dòng)作存在。并發(fā)帶來的問題在享受并發(fā)編程帶來的高性能高吞吐量的同時(shí),也會(huì)因?yàn)椴l(fā)編程帶來一些意想不到弊端。并發(fā)過程中多線程之間的切換調(diào)度,上下文的保存恢復(fù)等都會(huì)帶來額外的線程切換開銷。 0x01 什么是并發(fā) 要理解并發(fā)首選我們來區(qū)分下并發(fā)和并行的概念。 并發(fā):表示在一段時(shí)間內(nèi)有多個(gè)動(dòng)作存在。 并行:表示在同一時(shí)間點(diǎn)有多個(gè)動(dòng)作同時(shí)存在。 例如:此刻我正在寫博客,但...

    pcChao 評(píng)論0 收藏0
  • 多線程編程完全指南

    摘要:在這個(gè)范圍廣大的并發(fā)技術(shù)領(lǐng)域當(dāng)中多線程編程可以說是基礎(chǔ)和核心,大多數(shù)抽象并發(fā)問題的構(gòu)思與解決都是基于多線程模型來進(jìn)行的。一般來說,多線程程序會(huì)面臨三類問題正確性問題效率問題死鎖問題。 多線程編程或者說范圍更大的并發(fā)編程是一種非常復(fù)雜且容易出錯(cuò)的編程方式,但是我們?yōu)槭裁催€要冒著風(fēng)險(xiǎn)艱辛地學(xué)習(xí)各種多線程編程技術(shù)、解決各種并發(fā)問題呢? 因?yàn)椴l(fā)是整個(gè)分布式集群的基礎(chǔ),通過分布式集群不僅可以大...

    mengera88 評(píng)論0 收藏0
  • 并發(fā)

    摘要:表示的是兩個(gè),當(dāng)其中任意一個(gè)計(jì)算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)??梢娝氖褂?,在開始分析它的高并發(fā)實(shí)現(xiàn)機(jī)制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購,是兩個(gè)比較典型的互聯(lián)網(wǎng)高并發(fā)場景。 干貨:深度剖析分布式搜索引擎設(shè)計(jì) 分布式,高可用,和機(jī)器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來,我們一步一步來擊破前兩個(gè)名詞,今天我們首先來說說分布式。 探究...

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

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

0條評(píng)論

閱讀需要支付1元查看
<