成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

對(duì)Java多線程的一些理解

Nekron / 2639人閱讀

摘要:線程僅僅被視為一個(gè)與其他進(jìn)程共享某些資源的進(jìn)程。穩(wěn)定性可創(chuàng)建的線程的數(shù)量上存在限制,包括的啟動(dòng)參數(shù)操作系統(tǒng)對(duì)線程的限制,如果超出這些限制,很可能會(huì)拋出異常。若是密集型程序產(chǎn)生大量的線程切換,將會(huì)降低系統(tǒng)的吞吐量。

OS中的進(jìn)程、線程

進(jìn)程:即處于執(zhí)行期的程序,且包含其他資源,如打開(kāi)的文件、掛起的信號(hào)、內(nèi)核內(nèi)部數(shù)據(jù)、處理器狀態(tài)、內(nèi)核地址空間、一個(gè)或多個(gè)執(zhí)行的線程、數(shù)據(jù)段。

線程:進(jìn)程中的活動(dòng)對(duì)象,內(nèi)核調(diào)度的對(duì)象不是進(jìn)程而是線程;傳統(tǒng)Unix系統(tǒng)一個(gè)進(jìn)程只包含一個(gè)線程。

線程在Linux中的實(shí)現(xiàn)

從Linux內(nèi)核的角度來(lái)說(shuō),并沒(méi)有線程這個(gè)概念。Linux把所有的線程都當(dāng)做進(jìn)程來(lái)實(shí)現(xiàn),內(nèi)核沒(méi)有為線程準(zhǔn)備特別的調(diào)度算法和特別的數(shù)據(jù)結(jié)構(gòu)。線程僅僅被視為一個(gè)與其他進(jìn)程共享某些資源的進(jìn)程。所以,在內(nèi)核看來(lái),它就是一個(gè)普通的進(jìn)程。

在Windows或Solaris等操作系統(tǒng)的實(shí)現(xiàn)中,它們都提供了專門支持線程的機(jī)制(lightweight processes)。

寫時(shí)拷貝

傳統(tǒng)的fork()系統(tǒng)調(diào)用直接把所有資源復(fù)制給新創(chuàng)建的進(jìn)程,效率十分低下,因?yàn)榭截惖臄?shù)據(jù)也許并不需要。

Linux的fork()使用寫時(shí)拷貝實(shí)現(xiàn)。內(nèi)核此時(shí)并不復(fù)制整個(gè)進(jìn)程地址空間,而是讓父進(jìn)程和子進(jìn)程共享一個(gè)拷貝。

只有在需要寫入的時(shí)候,數(shù)據(jù)才會(huì)被復(fù)制,在此之前,只是以只讀方式共享。這種優(yōu)化可以避免拷貝大量根本就不會(huì)被使用的數(shù)據(jù)(地址空間常常包含幾十M的數(shù)據(jù))。

因此,Linux創(chuàng)建進(jìn)程和線程的區(qū)別就是共享的地址空間、文件系統(tǒng)資源、文件描述符、信號(hào)處理程序等這些不同。

以下是StackOverflow上的一個(gè)答案:

即,在Linux下,進(jìn)程使用fork()創(chuàng)建,線程使用pthread_create()創(chuàng)建;fork()pthread_create()都是通過(guò)clone()函數(shù)實(shí)現(xiàn),只是傳遞的參數(shù)不同,即共享的資源不同。(Linux是通過(guò)NPTL實(shí)現(xiàn)POSIX Thread規(guī)范,即通過(guò)輕量級(jí)進(jìn)程實(shí)現(xiàn)POSIX Thread,使之前在Unix上的庫(kù)、軟件可以平穩(wěn)的遷移到Linux上)

Java線程如何映射到OS線程

JVM在linux平臺(tái)上創(chuàng)建線程,需要使用pthread 接口。pthread是POSIX標(biāo)準(zhǔn)的一部分它定義了創(chuàng)建和管理線程的C語(yǔ)言接口。Linux提供了pthread的實(shí)現(xiàn):

pthread_t tid;
if (pthread_create(&tid, &attr, thread_entry_point, arg_to_entrypoint))
{
      fprintf(stderr, "Error creating thread
");
      return;
}

tid是新創(chuàng)建線程的ID

attr是我們需要設(shè)置的線程屬性

thread_entry_point是會(huì)被新創(chuàng)建線程調(diào)用的函數(shù)指針

arg_to_entrypoint是會(huì)被傳遞給thread_entry_point的參數(shù)

thread_entry_point所指向的函數(shù)就是Thread對(duì)象的run方法。

無(wú)返回值線程和帶返回值的線程

無(wú)返回值:一種是直接繼承Thread,另一種是實(shí)現(xiàn)Runnable接口

帶返回值:通過(guò)Callable和Future實(shí)現(xiàn)

帶返回值的線程是我們?cè)趯?shí)踐中更常用的。

競(jìng)態(tài)條件

當(dāng)某個(gè)計(jì)算的正確性取決于多個(gè)線程的交替執(zhí)行時(shí)序時(shí),那么就會(huì)發(fā)生競(jìng)態(tài)條件。

最常見(jiàn)的競(jìng)態(tài)條件類型就是“先檢查后執(zhí)行”(Check-Then-Act)操作,即通過(guò)一個(gè)可能失效的觀測(cè)結(jié)果來(lái)決定下一步的動(dòng)作。

使用“”先檢查后執(zhí)行“的一種常見(jiàn)情況就是延遲初始化:

public class LazyInitRace {
    private ExpensiveObject instance = null;
    
    public ExpensiveObject getInstance() {
        if (instance == null) {
            instance = new ExpensiveObject();
        }
        return instance;
    }
}

不要這么做。

Executor框架 使用裸線程的缺點(diǎn)

prod環(huán)境中,為每個(gè)任務(wù)分配一個(gè)線程的方法存在嚴(yán)重的缺陷,尤其是當(dāng)需要?jiǎng)?chuàng)建大量的線程時(shí):

線程生命周期的開(kāi)銷非常高:線程的創(chuàng)建與銷毀并不是沒(méi)有代價(jià)的。

資源消耗:會(huì)消耗內(nèi)存和CPU,大量的線程競(jìng)爭(zhēng)CPU資源將產(chǎn)生性能開(kāi)銷。如果你已經(jīng)擁有足夠多的線程使所有CPU處于忙碌狀態(tài),那么創(chuàng)建更多的線程反而會(huì)降低性能。

穩(wěn)定性:可創(chuàng)建的線程的數(shù)量上存在限制,包括JVM的啟動(dòng)參數(shù)、操作系統(tǒng)對(duì)線程的限制,如果超出這些限制,很可能會(huì)拋出OutOfMemoryError異常。

Executor基本原理

Executor基于生產(chǎn)者-消費(fèi)者模式,提交任務(wù)的操作相當(dāng)于生產(chǎn)者,執(zhí)行任務(wù)的線程則相當(dāng)于消費(fèi)者。

線程池的構(gòu)造函數(shù)如下:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }
線程池大小

corePoolSize:核心線程數(shù),當(dāng)線程池的線程數(shù)小于corePoolSize,直接創(chuàng)建新的線程

線程數(shù)大于corePoolSize但是小于maximumPoolSize:如果任務(wù)隊(duì)列還未滿, 則會(huì)將此任務(wù)插入到任務(wù)隊(duì)列末尾;如果此時(shí)任務(wù)隊(duì)列已滿, 則會(huì)創(chuàng)建新的線程來(lái)執(zhí)行此任務(wù)。

線程數(shù)等于maximumPoolSize:如果任務(wù)隊(duì)列還未滿, 則會(huì)將此任務(wù)插入到任務(wù)隊(duì)列末尾;如果此時(shí)任務(wù)隊(duì)列已滿, 則會(huì)由RejectedExecutionHandler處理。

keep-alive

keepAliveTime:當(dāng)我們的線程池中的線程數(shù)大于corePoolSize時(shí), 如果此時(shí)有線程處于空閑(Idle)狀態(tài)超過(guò)指定的時(shí)間(keepAliveTime), 那么線程池會(huì)將此線程銷毀。

工作隊(duì)列

工作隊(duì)列(WorkQueue)是一個(gè)BlockingQueue, 它是用于存放那些已經(jīng)提交的, 但是還沒(méi)有空余線程來(lái)執(zhí)行的任務(wù)。

常見(jiàn)的工作隊(duì)列有一下幾種:

直接切換(Direct handoffs)

無(wú)界隊(duì)列(Unbounded queues)

有界隊(duì)列(Bounded queues)

在生產(chǎn)環(huán)境中,禁止使用無(wú)界隊(duì)列,因?yàn)楫?dāng)隊(duì)列中堆積的任務(wù)太多時(shí),會(huì)消耗大量?jī)?nèi)存,最后OOM;通常都是設(shè)定固定大小的有界隊(duì)列,當(dāng)線程池已滿,隊(duì)列也滿的情況下,直接將新提交的任務(wù)拒絕,拋RejectedExecutionException 出來(lái),本質(zhì)上這是對(duì)服務(wù)自身的一種保護(hù)機(jī)制,當(dāng)服務(wù)已經(jīng)沒(méi)有資源來(lái)處理新提交的任務(wù),因直接將其拒絕。

Java原生線程池在生產(chǎn)環(huán)境中的問(wèn)題

在服務(wù)化的背景下,我們的框架一般都會(huì)集成全鏈路追蹤的功能,用來(lái)串聯(lián)整個(gè)調(diào)用鏈,主要是記錄TraceIdSpanId;TraceIdSpanId一般都記錄在ThreadLocal中,對(duì)業(yè)務(wù)方來(lái)說(shuō)是透明的。

當(dāng)在同一個(gè)線程中同步RPC調(diào)用的時(shí)候,不會(huì)存在問(wèn)題;但如果我們使用線程池做客戶端異步調(diào)用時(shí),就會(huì)導(dǎo)致Trace信息的丟失,根本原因是Trace信息無(wú)法從主線程的ThreadLocal傳遞到線程池的ThreadLocal中。

對(duì)于這個(gè)痛點(diǎn),阿里開(kāi)源的transmittable-thread-local解決了這個(gè)問(wèn)題,實(shí)現(xiàn)其實(shí)不難,可以閱讀一下源碼:

https://github.com/alibaba/transmittable-thread-local
性能與伸縮性 對(duì)性能的思考

提升性能意味著用更少的資源做更多的事情?!百Y源”的含義很廣,例如CPU時(shí)鐘周期、內(nèi)存、網(wǎng)絡(luò)帶寬、磁盤空間等其他資源。當(dāng)操作性能由于某種特定的資源而受到限制時(shí),我們通常將該操作稱為資源密集型的操作,例如,CPU密集型、IO密集型等。

使用多線程理論上可以提升服務(wù)的整體性能,但與單線程相比,使用多線程會(huì)引入額外的性能開(kāi)銷。包括:線程之間的協(xié)調(diào)(例如加鎖、觸發(fā)信號(hào)以及內(nèi)存同步),增加的上下文切換,線程的創(chuàng)建和銷毀,以及線程的調(diào)度等。如果過(guò)度地使用線程,其性能可能甚至比實(shí)現(xiàn)相同功能的串行程序更差。

從性能監(jiān)視的角度來(lái)看,CPU需要盡可能保持忙碌狀態(tài)。如果程序是計(jì)算密集型的,那么可以通過(guò)增加處理器來(lái)提升性能。但如果程序無(wú)法使CPU保持忙碌狀態(tài),那增加更多的處理器也是無(wú)濟(jì)于事的。

可伸縮性

可伸縮性是指:當(dāng)增加計(jì)算資源時(shí)(例如CPU、內(nèi)存、存儲(chǔ)容量、IO帶寬),程序的吞吐量或者處理能力能響應(yīng)的增加。

我們熟悉的三層模型,即程序中的表現(xiàn)層、業(yè)務(wù)邏輯層和持久層是彼此獨(dú)立,并且可能由不同的服務(wù)來(lái)處理,這很好地說(shuō)明了提高伸縮性通常會(huì)造成性能損失。如果把表現(xiàn)層、業(yè)務(wù)邏輯層和持久層都融合到某個(gè)單體應(yīng)用中,在負(fù)載不高的時(shí)候,其性能肯定要高于將應(yīng)用程序分為多層的性能。這種單體應(yīng)用避免了在不同層次之間傳遞任務(wù)時(shí)存在的網(wǎng)絡(luò)延遲,減少了很多開(kāi)銷。

然而、當(dāng)單體應(yīng)用達(dá)到自身處理能力的極限時(shí),會(huì)遇到一個(gè)嚴(yán)重問(wèn)題:提升它的處理能力非常困難,即無(wú)法水平擴(kuò)展。

Amdahl定律

大多數(shù)并發(fā)程序都是由一系列的并行工作和串行工作組成的。Amdahl定律描述的是:在增加計(jì)算資源的情況下,程序在理論上能夠?qū)崿F(xiàn)最高加速比,這個(gè)值取決于程序中可并行組件串行組件所占的比重。假定F是必須被串行執(zhí)行的部分,那么根據(jù)Amdahl定律,在包含N個(gè)處理器的機(jī)器上,最高的加速比為:

當(dāng)N趨近于無(wú)窮大時(shí),最大的加速比趨近于1/F。因此,如果程序中有50%的計(jì)算需要串行執(zhí)行,那么最高的加速比只能是2。

上下文切換

線程調(diào)度會(huì)導(dǎo)致上下文切換,而上下文切換是會(huì)產(chǎn)生開(kāi)銷的。若是CPU密集型程序產(chǎn)生大量的線程切換,將會(huì)降低系統(tǒng)的吞吐量。

UNIX系統(tǒng)的vmstat命令能夠報(bào)告上下文切換次數(shù)以及在內(nèi)核中執(zhí)行時(shí)間的所占比例等信息。如果內(nèi)核占用率較高(超過(guò)10%),那么通常表示調(diào)度活動(dòng)發(fā)生得很頻繁,這很可能是由I/O或者鎖競(jìng)爭(zhēng)導(dǎo)致的阻塞引起的。

>> vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0      0 3235932 238256 3202776    0    0     0    11    7    4  1  0 99  0  0
 
 cs:每秒上下文切換次數(shù)
 sy:內(nèi)核系統(tǒng)進(jìn)程執(zhí)行時(shí)間百分比
 us:用戶進(jìn)程執(zhí)行時(shí)間百分比

以上。

原文鏈接

https://segmentfault.com/a/11...

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/71272.html

相關(guān)文章

  • 學(xué)習(xí)Java線程一些總結(jié)

    摘要:多線程環(huán)境下的一些問(wèn)題安全性問(wèn)題在沒(méi)有正確同步的情況下,多線程環(huán)境下程序可能得出錯(cuò)誤的結(jié)果。一些相關(guān)概念競(jìng)爭(zhēng)條件多線程的環(huán)境下,程序執(zhí)行的結(jié)果取決于線程交替執(zhí)行的方式。而線程的交替操作順序是不可預(yù)測(cè)的,如此程序執(zhí)行的結(jié)果也是不可預(yù)測(cè)的。 入口 Java多線程的應(yīng)用復(fù)雜性之如jvm有限的幾個(gè)內(nèi)存方面的操作和規(guī)范,就像無(wú)數(shù)紛繁復(fù)雜的應(yīng)用邏輯建立在有限的指令集上。 如何寫出線程安全的程序,有...

    coolpail 評(píng)論0 收藏0
  • 金三銀四,2019大廠Android高級(jí)工程師面試題整理

    摘要:原文地址游客前言金三銀四,很多同學(xué)心里大概都準(zhǔn)備著年后找工作或者跳槽。最近有很多同學(xué)都在交流群里求大廠面試題。 最近整理了一波面試題,包括安卓JAVA方面的,目前大廠還是以安卓源碼,算法,以及數(shù)據(jù)結(jié)構(gòu)為主,有一些中小型公司也會(huì)問(wèn)到混合開(kāi)發(fā)的知識(shí),至于我為什么傾向于混合開(kāi)發(fā),我的一句話就是走上編程之路,將來(lái)你要學(xué)不僅僅是這些,豐富自己方能與世接軌,做好全棧的裝備。 原文地址:游客kutd...

    tracymac7 評(píng)論0 收藏0
  • jvm原理

    摘要:在之前,它是一個(gè)備受爭(zhēng)議的關(guān)鍵字,因?yàn)樵诔绦蛑惺褂盟占骼斫夂驮矸治龊?jiǎn)稱,是后提供的面向大內(nèi)存區(qū)數(shù)到數(shù)多核系統(tǒng)的收集器,能夠?qū)崿F(xiàn)軟停頓目標(biāo)收集并且具有高吞吐量具有更可預(yù)測(cè)的停頓時(shí)間。 35 個(gè) Java 代碼性能優(yōu)化總結(jié) 優(yōu)化代碼可以減小代碼的體積,提高代碼運(yùn)行的效率。 從 JVM 內(nèi)存模型談線程安全 小白哥帶你打通任督二脈 Java使用讀寫鎖替代同步鎖 應(yīng)用情景 前一陣有個(gè)做...

    lufficc 評(píng)論0 收藏0
  • Java開(kāi)發(fā)

    摘要:大多數(shù)待遇豐厚的開(kāi)發(fā)職位都要求開(kāi)發(fā)者精通多線程技術(shù)并且有豐富的程序開(kāi)發(fā)調(diào)試優(yōu)化經(jīng)驗(yàn),所以線程相關(guān)的問(wèn)題在面試中經(jīng)常會(huì)被提到。將對(duì)象編碼為字節(jié)流稱之為序列化,反之將字節(jié)流重建成對(duì)象稱之為反序列化。 JVM 內(nèi)存溢出實(shí)例 - 實(shí)戰(zhàn) JVM(二) 介紹 JVM 內(nèi)存溢出產(chǎn)生情況分析 Java - 注解詳解 詳細(xì)介紹 Java 注解的使用,有利于學(xué)習(xí)編譯時(shí)注解 Java 程序員快速上手 Kot...

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

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

0條評(píng)論

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