摘要:并發編程的核心是為了提高電腦資源的利用率,因為現代操作系統都是多核的,可以同時跑多個線程。合理配置線程池,密集型任務配置少數線程池如個數,密集型任務配置多一點的線程池如個數,其次是使用有界隊列即使發現錯誤。
并發編程的核心是為了提高電腦資源的利用率,因為現代操作系統都是多核的,可以同時跑多個線程。那么是不是線程越多越好? 由于線程的切換涉及上下文的切換,所謂上下文就是線程運行時需要的資源,系統要分配給它消耗時間。所以為了減少上下文的切換,我們有以下幾種方法:
CAS算法
協程,單線程里實現多任務調度
避免創建不需要的線程因此
協程和線程區別:每個線程OS會給它分配固定大小的內存(一般2MB)來存儲當前調用或掛起的函數的內部變量,固定大小的棧意味著內存利用率很低或有時面對復雜函數無法滿足要求,協成就實現了可動態伸縮的棧(最小2KB,最大1GB).其二OS線程受操作系統調度,調度時要將當前線程狀態存到內存,將另一個線程執行指令放到寄存器,這幾步很耗時。Go調度器并非硬件調度器,而是Go語言內置的一中機制,因此goroutine調度時則不需要切換上下文。
Java并發機制的底層實現原理,java代碼編譯成字節碼后加載到JVM中,JVM執行字節碼最終轉化成匯編命令在CPU上運行,因此Java所使用的并發機制依賴JVM的實現和CPU指令。Java大部分并發容器和框架都依賴于volatile和原子操作的實現原理。
volatile:被volatile修身的變量在進行寫操作時會多出一行以Lock為前綴的匯編代碼,Lock前綴的指令在多核處理器下執行兩件事情,1.將當前處理器緩存行(緩存可分配的最小單元)的數據寫入到系統內2.寫回內存的操作使其它處理器地址為該緩存的內存無效。這兩條保證了所謂的可見性
原子操作的實現:首先看一看處理器是如何實現原子操作的,有兩核CPU1和CPU2,兩個處理器同時對數據i進行操作,CPU采取總線鎖使得一個數據不能同時被多個處理器操作。大概原理就是使用處理器提供的一個LOCK信號,一個處理器在總線上輸出此信號時另一個處理器的請求被阻塞住。這樣會導致別的處理器不能處理其它內存地址的數據,因為總線鎖開銷比較大出現了緩存鎖,使得CPU1修改緩存行1中數據時若使用了緩存鎖定,那么CPU2就不能再緩存該緩存。處理器提供了一系列命令支持這兩種機制,如BTS,XADD等,被這些指令操作的內存區域就會加鎖,使其它處理器不能同時訪問。
Java內存模型Java之間通過共享內存進行通信,處理器和編譯器為了提高性能會對指令進行重排序,這在單線程情況下不會發生異常,但是在多線程下就會造成結果的不一致
int a=0; public int calculate(){ a=1; 1 boolean flag=true; 2 if(flag){ return a*a; } return 0; }
現有兩個線程執行這段代碼,線程A執行時對指令進行了重排序先制行 2 在執行 1,在中間線程B插入了進來此時a=1值還沒被寫入導致返回結果為0發生錯誤。
處理器遵循as-if-serial語義,即不管如何重排序結果不變,但是多線程情況下會出現錯誤
為了避免重排序,Java引入了volatile變量,使得語句在操作被volatile修飾的變量時禁止指令重排序。在執行指令時插入內存屏障也就是這個目的,最關鍵的是volatile的讀/寫內存語義如下
寫語義:寫一個volatile變量時會把線程對應本地內存的值刷新到主存中
讀語義:讀一個volatile變量時會把本地內存的值設置為無效,從主存中讀
volatile的缺陷在于改這個動作是不完全的,因此又提出了CAS機制,CAS會使用處理器提供的機器級別的原子命令(CMPXCHG),原子執行讀-改-寫操作。Java concurrent包中一個通用化的實現模式就是結合兩者,步驟如下
聲明共享變量為volatile
使用CAS實現線程間的同步和通信,(自旋樂觀鎖,性能大大提升)
Java線程池線程池的核心作用就是維護固定的幾個線程,有任務來的時候直接使用避免創建/銷毀線程導致的額外開銷。 線程池執行流程如下:
提交任務-->核心線程池已滿? 是 提交任務到消息隊列--->隊列已滿? 是 按指定策略執行 否 創建線程執行任務 否 加進隊列
了解了線程池的原理最重要的就是如何是去使用它,而使用的關鍵就是參數的設置。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueworkQueue, 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的構造函數,我們逐一看一看各參數的含義
corePoolSize 一直維護的線程數
maximumPoolSize 最大線程數
keepAliveTime 多余線程存活的時間(實際線程數比corePool多的那部分)
workQueue 存儲線程的隊列,可選擇ArrayBlockingQueue等
threadFactory 創建線程時的用到的工廠,可通過自定義工廠創建更有意義的線程名稱
handler 隊列滿時采取的策略 有AbortPolicy(直接拋出異常)/CallerRunsPolicy(只用調用者所在的線程執行)等等
提交線程池有兩個方法,一個是submit這個不需要返回值,一個是submit會返回一個future對象,并通過future的get()方法獲取返回值(該方法會阻塞直到線程完成任務)。
合理配置線程池,CPU密集型任務配置少數線程池如N(CPU個數)+1,I/O密集型任務配置多一點的線程池如2N(CPU個數),其次是使用有界隊列即使發現錯誤。
Executor框架在HotSpot VM的線程模型中,Java線程被一對一的映射成本地操作系統的線程,操作系統會調度線程把它們分配給可用的CPU。在上層Java通過用戶級調度器Executor將任務映射為幾個線程,在下層操作系統內核將這些線程映射到硬件處理器上面。
Executor的出現將任務與如何執行任務分離開了,避免了每創建一個線程就要執行它。Executor的整個架構有一下幾個要點
實現了Runnable和Callable的對象可提交到Executor運行
可返回Future獲取線程執行后的返回值
內部維護一個線程池(上面介紹的)來處理提交過來的任務
Executor最核心的就是ThreadPoolExecutor,下面介紹以下以及各自使用場景
FixedThreadPool 固定線程個數,用于高負載的服務器,滿足資源的管理需求
SingleThreadPool 單個線程,保證順序的執行任務
CachedThreadPool 大小無界的線程池,使用負載比較輕的服務器
ScheduledThreadPoolExecutor 后臺周期執行任務
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/68960.html
摘要:相比與其他操作系統包括其他類系統有很多的優點,其中有一項就是,其上下文切換和模式切換的時間消耗非常少。因為多線程競爭鎖時會引起上下文切換。減少線程的使用。很多編程語言中都有協程。所以如何避免死鎖的產生,在我們使用并發編程時至關重要。 系列文章傳送門: Java多線程學習(一)Java多線程入門 Java多線程學習(二)synchronized關鍵字(1) java多線程學習(二)syn...
摘要:因為多線程競爭鎖時會引起上下文切換。減少線程的使用。舉個例子如果說服務器的帶寬只有,某個資源的下載速度是,系統啟動個線程下載該資源并不會導致下載速度編程,所以在并發編程時,需要考慮這些資源的限制。 最近私下做一項目,一bug幾日未解決,總惶恐。一日頓悟,bug不可怕,怕的是項目不存在bug,與其懼怕,何不與其剛正面。 系列文章傳送門: Java多線程學習(一)Java多線程入門 Jav...
摘要:并發表示在一段時間內有多個動作存在。并發帶來的問題在享受并發編程帶來的高性能高吞吐量的同時,也會因為并發編程帶來一些意想不到弊端。并發過程中多線程之間的切換調度,上下文的保存恢復等都會帶來額外的線程切換開銷。 0x01 什么是并發 要理解并發首選我們來區分下并發和并行的概念。 并發:表示在一段時間內有多個動作存在。 并行:表示在同一時間點有多個動作同時存在。 例如:此刻我正在寫博客,但...
閱讀 2975·2021-11-24 10:22
閱讀 3044·2021-11-23 10:10
閱讀 1353·2021-09-28 09:35
閱讀 1752·2019-08-29 13:16
閱讀 1395·2019-08-26 13:29
閱讀 2782·2019-08-26 10:27
閱讀 678·2019-08-26 10:09
閱讀 1436·2019-08-23 18:05