Java 併發

目錄

(1)基礎概念

(2)線程

(3)鎖

(4)同步器

(5)併發容器和框架

(6)Java併發工具類

(7)原子操作類

(8)Executor框架(執行機制)

(9)其他


(一).基礎概念

1.可見性和原子性

  • 可見性:一個線程修改了共享變量的值,另一個線程可以讀到這個修改的值。
  • 原子性:不可被中斷的一個或一系列操作。

如何保障原子性:

  • 使用總線鎖保證原子性。
  • 使用緩存鎖保證原子性。

2.原子操作的三種實現:

(1).CAS(Compare And Swap 比較與交換)

需要輸入兩個數值(一箇舊值和一個新值),在操作期間先比較舊值有沒有發生變化,如果沒有發生變化,才交換成新值,如果發生了變化就不交換。

存在的三大問題:

  • ABA問題:如果一個值原來是A,變成了B,又變成了A,那麼使用CAS進行檢查時會發現它的值沒有發生變化,但是實際是發生了變化。解決方案:1.使用版本號,在變量前面追加版本號,每次變量更新都把版本號加1。JDK提供的類:AtomicStampedReference。
  • 循環時間長開銷大。
  • 只能保證一個共享變量的原子操作。

解決方案:JDK提供AtomicReference類來保證引用對象之間的原子性,可以把多個變量放在一個對象裡進行CAS操作。

(2).鎖

(3).JDK併發包的支持

如:AtomicBoolean(用原子方式更新的boolean值),

AtomicInteger(用原子方式更新的int值),

AutomicLong(用原子方式更新的long值)。

3.同步原語

(1).volatile

特點:

  • 可見性:對一個volatile變量的讀,總是能看到任意線程對這個volatile變量最後的寫入。
  • 原子性:對任意單個volatile變量的讀/寫具有原子性。
  • 從內存語義角度:volatile的寫-讀與鎖的釋放-獲取有相同的內存效果。
  • 為了實現volatile的內存語義,編譯期在生成字節碼時,會在指令序列中插入內存屏障來禁止特定類型的處理器重排序。
  • 從編譯器重排序規則和處理器內存屏障插入策略來看,只要volatile變量與普通變量之間的重排序可能會破壞volatile的內存語義,這種重排序就會被編譯器重排序規則和處理器內存屏障插入策略禁止。

實現原理:

  • 將當前處理器緩存行的數據回寫到系統內存。
  • 寫回內存的操作會使其他CPU裡緩存該內存地址的數據無效。

(2).synchronized

不同情況鎖住的對象:

  • 對於普通同步方法,鎖是當前實例對象。
  • 對於靜態同步方法,鎖是當前類的Class對象。
  • 對於同步方法塊,鎖是Synchronized括號裡配置的對象。

(3)final

public class FinalExample { int i; //普通變量 final int j; //final變量 static FinalExample obj;  public FinalExample() { //構造函數 i = 1; //寫普通域 j = 2; //寫final域 }  public static void writer() { obj = new FinalExample(); }  public static void reader() { FinalExample object = obj; //讀對象引用 int a = object.i; //讀普通域 int n = object.j; //讀final域 }}
  • 寫final域的重排序規則:JMM禁止將final域的寫重排序到構造函數之外。
  • 讀final域的重排序規則:在一個線程中,初次讀對象引用與初次讀該對象包含的final域,JMM禁止處理器重排序這兩個操作。

4.Java內存模型(JMM)

5.重排序

重排序的3種類型:

  • 編譯器優化的重排序。
  • 指令級並行的重排序。
  • 內存系統的重排序。

編譯器和處理器在重排序時,會遵守數據依賴性,編譯器和處理器不會改變存在數據依賴關係的兩個操作的執行順序。

6.順序一致性

順序一致性內存模型兩大特徵:

  • 一個線程中的所有操作必須按照程序的順序來執行。
  • (不管程序是否同步)所有線程都只能看到一個單一的操作執行順序。在順序一致性內存模型中,每個操作都必須原子執行且立刻對所有線程可見。

7.happens-before與as-if-serial

JMM對happens-before的設計原則:只要不改變程序的執行結果(指的是單線程程序和正確同步的多線程程序),編譯器和處理器怎麼優化都行。

happens-before關係的定義:

(1)如果一個操作happens-before另一個操作,那麼第一個操作的執行結果將對第二個操作可見,而且第一個操作的執行順序排在第二個操作之前。

(2)兩個操作之間存在happens-before關係,並不意味著Java平臺的具體實現必須要按照happens-before關係指定的順序來執行。如果重排序之後的執行結果,與按happens-before關係來執行的結果一致,那麼JMM允許這種重排序。

as-if-serial:不管怎麼重排序(編譯器和處理器為了提高並行度),(單線程)程序的執行結果不能被改變。

區別:as-if-serial語義保障單線程內程序的執行結果不被改變,happens-before關係保證正確同步的多線程程序的執行結果不被改變。

8.雙重檢查鎖定與延遲初始化

使用雙重檢查鎖延遲初始化

public class DoubleCheckedLocking {   private static DoubleCheckedLocking instance;   public static DoubleCheckedLocking getInstance() {  if(null == instance) {  synchronized(DoubleCheckedLocking.class) {  if(null == instance) {  instance = new DoubleCheckedLocking();  } } } return instance; }}

線程A-A1:分配對象的內存空間。

線程A-A2:設置instance指向內存空間。

線程B-B1:判斷instance是否為空。

線程B-B2:由於instance不為null,線程B將訪問instance引用的對象。

線程A-A3:初始化對象

線程A-A4:訪問instance引用的對象。

存在的問題:

A2和A3重排序,線程B訪問到一個還沒初始化的對象。

解決方案:

  • 將instance變量聲明為volatile型的。通過禁止重排序來保證線程安全的延遲初始化。
  • 通過不允許其他線程”看到“這個重排序實現線程安全的延遲初始化。
public class InstanceFactory { private static class InstanceHolder { public static InstanceFactory instance = new InstanceFactory(); }  public static InstanceFactory getInstance() { return InstanceHolder.instance; }}

9.生產者-消費者模式


(二).線程

1.什麼是線程?

現代操作系統在運行一個程序時,會為其創建一個進程。現代操作系統調度的最小單元是線程,也叫輕量級進程。在一個進程裡可以創建多個線程,這些線程都擁有各自的計數器,堆棧和局部變量等屬性。

2.創建線程的三種方式

(1).Thread

@Testpublic void testThread() { Thread thread = new Thread("myThread"); thread.start();}

(2).Runnable

@Testpublic void testRunnable() { Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("myThread"); }  }); thread.start();}

(3).Callable

@Testpublic void testFutureTask() throws InterruptedException, ExecutionException { ExecutorService executorService = Executors.newFixedThreadPool(2); Future future = executorService.submit(new Callable() { @Override public String call() throws Exception { return "Hello,World!!!"; }  }); String result = future.get(); System.out.println(result); executorService.shutdown(); }

3.Daemon線程

  • Daemon線程是一種支持型線程,因為它主要被用作程序中後臺調度以及支持性工作。
  • 當一個Java虛擬機中不存在非Daemon線程的時候,Java虛擬機將會退出。
  • 可以通過調用Thread,setDaemon(true)將線程設置為Daemon線程。

4.等待/通知機制

等待/通知機制,是指一個線程A調用了對象O的wait()方法進入等待狀態,而另一個線程B調用了對象O的notify()或者notifyAll()方法,線程A收到通知後從對象O的wait()方法返回,進而執行後續操作。

等待方遵循如下規則:

  • 獲取對象的鎖
  • 如果條件不滿足,那麼調用對象的wait()方法,被通知後仍要檢查條件。
  • 條件滿足則執行對應的邏輯。
while(條件不滿足){ 對象.wait();}//處理對應的邏輯

通知方遵循如下規則:

  • 獲得對象的鎖。
  • 改變條件
  • 通知所有等待在對象上的線程。
synchronized(對象){ //改變條件 對象.notifyAll();}

5.Thread.join()

當前線程A要等待thread線程終止之後才能從thread.join()返回。

6.ThreadLocal

ThreadLocal,即線程變量,是一個以ThreadLocal對象為鍵,任意對象為值的存儲結構。這個結構被附帶在線程上,也就是說一個線程可以根據一個ThreadLocal對象查詢到綁定到這個線程上的一個值。

7.線程的終止、中斷

(1).Thread.interrupt:中斷線程

  • 除非線程正在進行中斷它自身,否則都會接受這個方法的中斷。會調用Thread.checkAccess(),可能會拋出SecurityException。
  • 如果線程調用了Object.wait(),Thread.sleep(),Thread.join()處於阻塞狀態,那它的堵塞狀態會被清除,並得到一個InterruptedException。
  • 如果線程在InterruptibleChannel上的I/O操作中被中斷,通道會被關閉,線程的中斷狀態會被設置,並得到一個ClosedByInterruptedException。

(2).Thread.interrupted:測試當前線程是否被中斷。

清除線程的中斷狀態。如果連續調用兩次這個方法,第二次調用會返回false(除非當前線程再次被中斷,在第一次調用清除它的中斷狀態之後,並且在第二次調用檢查它之前)。

(3).Thread.isInterrupted:測試某個線程是否被中斷

中斷狀態是否被重置取決於傳入的值。


(三).鎖

鎖是Java併發編程中最重要的同步機制。鎖除了讓臨界區互斥執行外,還可以讓釋放鎖的線程向獲取同一個鎖的線程發送消息。

1.鎖的內存語義:

  • 利用volatile變量的寫-讀所具有的內存語義。
  • 利用CAS所附帶的volatile讀和volatile寫的內存語義。

2.重入鎖

(1).什麼是重入鎖?

支持重進入的鎖,表示鎖能夠支持一個線程對資源的重複加鎖。重入鎖支持獲取鎖時的公平性和非公平性選擇。

(2).解決兩個問題:

  • 線程再次獲取鎖:鎖需要去識別獲取鎖的線程是否為當前佔據鎖的線程,如果是,則再次獲取鎖。
  • 鎖的最終釋放:鎖的最終釋放要求鎖對於鎖獲取進行計數自增,計數表示當前鎖被重複獲取的次數,而鎖被釋放時,計數自減,當計數等於0時表示鎖已經釋放。

3.排他鎖:ReentrantLock

(1)公平鎖:

  • 公平鎖釋放時,最後要寫一個volatile變量state。
  • 公平鎖獲取時,首先會去讀volatile變量。

(2)非公平鎖:

  • 非公平鎖釋放時,最後要寫一個volatile變量state。
  • 非公平鎖獲取時,首先會用CAS(CompareAndSet)更新volation變量,這個操作同時具有volatile寫和volatile讀的內存語義。

(3)公平鎖與非公平鎖的區別

公平性與否是針對獲取鎖而言的。

  • 公平鎖:如果一個鎖是公平的,那麼獲取鎖的順序就應該符合請求的絕對時間順序,也就是FIFO。
  • 非公平鎖:剛釋放鎖的線程再次獲取同步狀態的幾率會非常大,使得其他線程只能在同步隊列中等待。

公平性鎖保證了鎖的獲取按照FIFO原則,而代價是進行大量的線程切換。非公平性鎖雖然可能造成”飢餓“,但極少的線程切換,保證其更大的吞吐量。

4.Lock

(1)讀寫鎖:ReentrantReadWriteLock

讀寫鎖在同一時刻可以允許多個讀線程訪問,但是在寫線程訪問時,所有的讀線程和其他寫線程均被堵塞。

讀寫鎖的實現分析:

  • 讀寫狀態的設計:同步狀態表示鎖被一個線程重複獲取的次數,而讀寫鎖的自定義同步需要在同步狀態(一個整型變量)上維護多個讀線程和一個寫線程的狀態,使得該狀態的設計成為讀寫鎖實現的關鍵。
  • 寫鎖的獲取與釋放:寫鎖是一個支持重進入的排它鎖。如果當前線程已經獲取了寫鎖,則增加寫狀態。如果當前線程在獲取寫鎖時,讀鎖已經被獲取(讀狀態不為0)或者該線程不是已經獲取寫鎖的線程,則當前線程進入等待狀態。
  • 讀鎖的獲取與釋放:如果當前線程已經獲取了讀鎖,就增加讀狀態。如果當前線程在獲取讀鎖時,寫鎖已被其他線程獲取,則進入等待狀態。
  • 鎖降級:鎖降級指的是寫鎖降級為讀鎖。指把持住(當前擁有的)寫鎖,再獲取到讀鎖,隨後釋放(先前擁有的)讀鎖的過程。

鎖的四種狀態:無鎖,偏向鎖,輕量級鎖,重量級鎖

5.LockSupport工具

當需要阻塞或喚醒一個線程的時候,都會使用LockSupport工具類來完成相應的工作。

6.Condition接口

Condition接口提供了類似Object的監視器方法(包括wait(),wait(long timeout),notify(),以及notifyAll()方法),與Lock配合可以實現等待/通知模式。

Condition的實現:等待隊列,等待和通知。

  • 等待隊列:等待隊列是一個FIFO隊列,在隊列中的每一個節點都包含了一個線程引用,該線程是在Condition對象上等待的線程,如果一個線程調用了Condition.await()方法,那麼該線程會釋放鎖,構造成節點加入等待隊列並進入等待狀態。
  • 等待:調用Condition的await()方法(或者以await開頭的方法),會使當前線程進入等待隊列並釋放鎖,同時線程狀態變為等待狀態。當從await()方法返回時,當前線程一定獲取了Condition相關的鎖。
  • 通知:調用Condition的signal()方法,將會喚醒在等待隊列中等待時間最長的節點(首節點),在喚醒節點之前,會將節點移步到同步隊列。

7.避免活躍性危險

(1).死鎖

  • 哲學家用餐問題:每個線程都擁有別的線程需要的資源,同時又等待別人擁有的資源,在獲得別的資源之前不會釋放自己手上的資源。
  • 數據庫事務死鎖:數據庫如果發生死鎖,會選擇一個事務釋放資源。
  • 鎖順序死鎖:線程A,B都需要鎖1,2。線程A先獲得鎖1 ,再請求鎖2 ,線程B先獲得鎖2,再請求鎖1 。

8.死鎖的避免與診斷

(1).內置鎖:只要沒有獲得鎖,就會一直等待下去。

(2).定時鎖:使用Lock類的定時tyLock功能。可以指定一個超時時限,在等待超過該時間後會返回失敗信息。

(3).線程轉儲

避免死鎖的四種方式:

  • 避免一個線程同時獲得多個鎖。
  • 避免一個線程在鎖內同時佔用多個資源,儘量保證每個鎖只獲得一個資源。
  • 使用定時鎖。
  • 對於數據庫鎖,加鎖和解鎖必須在一個數據庫連接裡,否則會出現解鎖失敗的情況。

9.飢餓,糟糕的響應性,活鎖

  • 飢餓:線程由於無法訪問它需要的資源而不能繼續執行,引發飢餓最常見的資源是CPU時鐘週期。
  • 活鎖通常發送在處理事務消息的應用程序中,如果不能成功地處理事務消息,那麼消息機制將回滾整個事務,將這個事務放在等待隊列的開頭。
  • 當多個相互協作的線程都對彼此響應從而修改各自的狀態,並使得任何一個線程都無法繼續執行時,就發生了活鎖。
  • 在併發應用中,通過等待隨機長度的時間和回退可以有效地避免活鎖的發生。

(四).同步器

(1).實現

  • 同步隊列:同步器依賴內部的同步隊列(一個FIFO雙向隊列)來完成同步狀態的管理,當前線程獲得同步狀態失敗時,同步器會將當前線程以及等待狀態等信息構造成為一個節點並將其加入同步隊列,同時會阻塞當前線程,當同步狀態釋放時,會把首節點中的線程喚醒,使其再次嘗試獲取同步狀態。
  • 獨佔式同步狀態獲取與釋放:在獲取同步狀態時,同步器維護一個隊列,獲取狀態失敗的線程會被加入隊列中並在隊列中進行自旋,移除隊列的原因時自旋獲取了同步狀態且前驅節點時頭節點。在釋放同步狀態時,同步器調用tryRelease(int arg)方法釋放同步狀態,然後喚醒頭節點的後繼結點。
  • 共享式同步狀態獲取與釋放:共享式獲取與獨佔式獲取最主要的區別在於同一時刻能否有多個線程同時獲取同步狀態。
  • 獨佔式超時獲取同步狀態:在指定的時間段內獲取同步狀態,如果獲取到同步狀態返回true,否則,返回false。

(2).AbstractQueuedSynchronized

用來構建鎖或者其他同步組件的基礎框架,使用了一個int成員變量表示同步狀態,通過內置的FIFO隊列來完成資源獲取線程的排隊工作。

提供了三個對同步狀態進行修改的方法:

  • getState():獲取當前同步狀態。
  • setState(int new3State):設置當前同步狀態。
  • compareAndSetState(int export,int update):使用CAS設置當前狀態,該方法能夠保證狀態設置的原子性。

(五).併發容器和框架

(1).ConcurrentHashMap

與HashMap,HashTable對比:

  • HashMap是線程不安全,且可能導致程序死循環。
  • HashTable效率低下。
  • ConcurrentHashMap的鎖分段技術可有效提升訪問效率。首先將數據分成一段一段的存儲,然後給每一段數據配一把鎖,當一個線程佔用鎖訪問其中一個段的數據的時候,其他段的數據也能被其他線程訪問。

ConCurrentHashMap的結構:ConCurrentHashMap是由Segment數組結構和HashEntry數組結構組成。

(2).ConcurrentLinkedQueue

ConcurrentLinkedQueue由head節點和tail節點組成,每個節點(Node)由節點元素(item)和指向下一個節點(next)的引用組成,從而組成一張鏈表結構的隊列。

(3).阻塞隊列

  • 插入:當隊列滿時,隊列會堵塞插入元素的線程,直到隊列不滿。
  • 移除:當隊列為空,獲取元素的線程會等待線程為非空。

實現原理:

使用通知模式實現。當生產者往滿的隊列裡添加元素時會堵塞生產者,當消費者消費了一個隊列中的元素後,會通知生產者當前隊列可用。

1.ArrayBlockingQueue

  • 數組實現的有界阻塞隊列,按照先進先出的原則對元素進行排序。

2.LinkedBlockingQueue

  • 繼承了AbstractQueue類,實現了BlockingQueue接口。
  • 採用先進先出的排列方式,頭結點是入隊時間最長的元素,尾結點是入隊時間最短的元素。新結點添加到隊尾,從隊頭彈出結點。
  • 鏈表隊列的特點是:跟基於數組的隊列相比有更大的吞吐量,但在大多併發應用中性能會比較差。
  • LinkedBlockingQueue可以在創建的時候傳遞一個容量參數,限制隊列的長度,不設定的情況下,默認是Integer.MAX_VALUE。在沒有超過隊列邊界的情況下,每次添加會自動創建鏈表結點。

3.PriorityBlockingQueue

  • 是一個支持優先級的無界阻塞隊列。默認情況下時自然順序升序排序。

4.SychronousQueue

  • SynchronousQueue是一個不存儲元素的堵塞隊列,每一個put操作必須等待一個take操作,否則不能繼續添加元素。

5.DelayQueue

  • 延遲隊列:無界隊列,只有延遲過期的任務才能加入隊列。隊列的隊首元素是在過去延遲過期最長的元素。如果沒有延遲到期,隊列中就沒有元素,調用poll方法會返回null。當調用元素的getDelay(TimeUnit.NANOSECONDS)方法返回等於或小於0的值時,出現到期。
  • DelayQueue的應用場景:a.緩存系統:把需要緩存的元素加入DelayQueue中,讓一個線程循環測試是否能從DelayQueue中獲取元素,能表示緩存到期。b.定時任務調度。
  • Timer和DelayQueue的區別?Timer只能處理單個後臺線程,而DelayQueue可以處理多個。

6 . LinkedTransferQueue

  • LinkedTransferQueue是一個由鏈表結構組成的無界阻塞TransferQueue隊列。

7 . LinkedBlockingDeque

  • 一個由鏈表結構組成的雙向阻塞隊列,可以運用在“工作竊取”模式中。

(4).Fork/Join框架

用於並行執行任務,把一個大任務分割成小任務,再把每個小任務的結果彙總成大任務結果。Fork是把一個大任務切分成若干子任務並行執行,Join是合併這些子任務的執行結果。

(5).工作竊取算法

指某個線程從其他隊列裡竊取任務來執行。為了減少竊取任務線程和別竊取任務線程之間的競爭,使用雙端隊列,被竊取任務線程從雙端隊列的頭部拿任務執行,竊取任務線程從雙端隊列的尾部拿任務執行。

  • 工作竊取算法的優點:充分利用線程進行並行計算,減少線程間的競爭。
  • 工作竊取算法的缺點:在某些情況下還是存在競爭,比如雙端隊列裡只有一個任務時,並且該算法會消耗更多的系統資源,比如創建多個線程和多個雙端隊列。

(六).Java併發工具類

1.CyclicBarrier

一組線程在到達一個屏障(同步點)前被堵塞,直到最後一個線程到達屏障時,屏障才會放行,這組線程才能繼續執行。

應用場景:可以用於多線程計算數據,最後合併計算結果。

CyclicBarrier與CountDownLatch的區別:CountDownLatch的計數器只能使用一次,而CyclicBarrier的計數器可以使用reset()方法重置。CountDownLatch的計數是減法,CyclicBarrier的計數是加法。

2.Semaphore

用來控制同時訪問特定資源的線程數量,通過協調各個線程,以保證合理的使用公共資源。

應用場景:可以用於流量控制,特別是公共資源有限的應用場景,比如數據庫連接。

3.CountDownLatch

允許一個或多個線程等待其他線程完成操作。

4.Exchanger

Exchanger是一個用於線程間協作的工具類。Exchanger用於進行線程間的數據交換。它提供一個同步點,在這個同步點,兩個線程可以交換彼此的數據。這兩個線程通過exchange方法交換數據,如果第一個線程先執行exchange()方法,會一直等待第二個線程也執行exchange()方法,當兩個線程都到達同步點時,這兩個線程就可以交換數據,將本線程生產出來的數據傳遞給對方。

應用場景:用於遺傳算法。


(七).原子操作類

1.原子更新基本類型類

  • AtomicBoolean
  • AtomicInteger
  • AtomicLong

2.原子更新數組

  • AtomicIntegerArray
  • AtomicLongArray
  • AtomicReferenceArray

3.原子更新引用類型

  • AtomicReference
  • AtomicReferenceFieldUpdater 原子更新引用類型裡的字段
  • AtomicMarkableReference 原子更新帶有標記位的引用類型。

4.原子更新字段類

  • AtomicIntegerFieldUpdater 原子更新整型的字段的更新器
  • AtomicLongFieldUpdater 原子更新長整型字段的更新器
  • AtomicStampedReference 原子更新帶有版本號的引用類型。該類將整數值與引用關聯起來,可用於原子的更新數據和數據的版本號,可以解決使用CAS進行原子更新時可能出現的ABA問題。

(八).Executor框架(執行機制)

從JDK5開始,把工作單元和執行機制分離開來,工作單元包括Runnable和Callable,而執行機制由Executor框架提供。

1.異步計算的結果:FutureTask和Future

2.任務執行

(1).Executor(核心接口)

Executor的生命週期:創建,提交,開始,完成

(2).ExecutorService接口(繼承自Executor)

  • ExecutorService的生命週期:運行,關閉,已終止
  • ExecutorService.shutDown和ExecutorService.shutDownNow的區別

調用的方法作用shutDown不再允許新的任務添加到等待隊列,正在執行的任務和在等待隊列中的任務會執行完畢再關閉。shurDownNow立刻關閉。需要知道正在執行但是還沒執行完畢的任務。

  • ExecutorService.submit()和ExecutorService.execute()的區別:接口ExecutorService的execute()方法是繼承自Executor。

調用的方法作用execute用於提交不需要返回值的任務,所以無法判斷任務是否被線程池執行成功。submit用於提交需要返回值的任務。線程池會返回一個Future類型的對象,通過這個Future對象可以判斷任務是否執行成功,並且通過Future的get()方法來獲取返回值,get()方法會阻塞線程直到任務完成。

  • ExecutorService的創建:

調用分類使用Executors.newSingleThreadExecutor()ThreadPoolExecutor應用場景:適用於需要保證順序地執行各個任務;並且在任意時間點,不會有多個線程活動的應用場景。Exectors.newFiexedThreadPool()ThreadPoolExecutor1.創建一個線程池,具有固定線程數,運行在共享的無界隊列中。2.在大多數時候,線程會主動執行任務,當所有的線程都在執行任務時,有新的任務加入進來,就會進入等待隊列(可以有源源不斷的任務加入進來,因為是無界隊列),當有空閒的線程,等待隊列中的任務就會被執行。3.如果有線程在執行過程中因為執行失敗要關閉,新創建的線程會替失敗的線程執行接下來的任務。4.如果想要關閉這個線程池,可以調用ExecutorService的shutDown方法。應用場景:適用於為了滿足資源管理的需求,而需要限制當前線程數量的應用場景,它適用於負載比較重的服務器。Executors.newCachedThreadPool()ThreadPoolExecutor應用場景:適用於執行很多短期異步任務的小程序,或者是負載較輕的服務器。Executors.newScheduledThreadPool()ScheduledThreadPoolExecutor應用場景:適用於需要多個後臺線程執行週期任務,同時為了滿足管理的需求而需要限制後臺線程的數量的應用場景。Executors.newSingleThreadScheduledExecutor()ScheduledThreadPoolExecutor應用場景:需要單個後臺線程執行週期任務,同時需要保證順序地執行各個任務。

3.任務:Runnable接口和Callable接口


(九).其他

1.jstack

打開cmd,輸入:

  • jps 查看pid(進程號)
  • jstack pid

2.資源限制:指在進行併發編程時,程序的執行速度受到計算機硬件資源或軟件資源的限制。

3.上下文切換

減少上下文切換的方法:

  • 無鎖併發編程
  • CAS算法
  • 使用最少線程
  • 使用協程


分享到:


相關文章: