日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java并发

發(fā)布時(shí)間:2024/1/17 java 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java并发 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

聲明:Java并發(fā)的內(nèi)容是自己閱讀《Java并發(fā)編程實(shí)戰(zhàn)》和《Java并發(fā)編程的藝術(shù)》整理來的。

個(gè)人網(wǎng)站

圖文并茂請(qǐng)戳

思維導(dǎo)圖下載請(qǐng)戳

目錄

(1)基礎(chǔ)概念

(2)線程

(3)鎖

(4)同步器

(5)并發(fā)容器和框架

(6)Java并發(fā)工具類

(7)原子操作類

(8)Executor框架(執(zhí)行機(jī)制)

(9)其他


(一).基礎(chǔ)概念

1.可見性和原子性

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

如何保障原子性:

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

2.原子操作的三種實(shí)現(xiàn):

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

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

存在的三大問題:

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

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

(2).鎖

(3).JDK并發(fā)包的支持

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

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

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

3.同步原語

(1).volatile

特點(diǎn):

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

實(shí)現(xiàn)原理:

  • 將當(dāng)前處理器緩存行的數(shù)據(jù)回寫到系統(tǒng)內(nèi)存。
  • 寫回內(nèi)存的操作會(huì)使其他CPU里緩存該內(nèi)存地址的數(shù)據(jù)無效。

(2).synchronized

不同情況鎖住的對(duì)象:

  • 對(duì)于普通同步方法,鎖是當(dāng)前實(shí)例對(duì)象。
  • 對(duì)于靜態(tài)同步方法,鎖是當(dāng)前類的Class對(duì)象。
  • 對(duì)于同步方法塊,鎖是Synchronized括號(hào)里配置的對(duì)象。

(3)final

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

4.Java內(nèi)存模型(JMM)

5.重排序

重排序的3種類型:

  • 編譯器優(yōu)化的重排序。
  • 指令級(jí)并行的重排序。
  • 內(nèi)存系統(tǒng)的重排序。

編譯器和處理器在重排序時(shí),會(huì)遵守?cái)?shù)據(jù)依賴性,編譯器和處理器不會(huì)改變存在數(shù)據(jù)依賴關(guān)系的兩個(gè)操作的執(zhí)行順序。

6.順序一致性

順序一致性內(nèi)存模型兩大特征:

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

7.happens-before與as-if-serial

JMM對(duì)happens-before的設(shè)計(jì)原則:只要不改變程序的執(zhí)行結(jié)果(指的是單線程程序和正確同步的多線程程序),編譯器和處理器怎么優(yōu)化都行。

happens-before關(guān)系的定義:

(1)如果一個(gè)操作happens-before另一個(gè)操作,那么第一個(gè)操作的執(zhí)行結(jié)果將對(duì)第二個(gè)操作可見,而且第一個(gè)操作的執(zhí)行順序排在第二個(gè)操作之前。

(2)兩個(gè)操作之間存在happens-before關(guān)系,并不意味著Java平臺(tái)的具體實(shí)現(xiàn)必須要按照happens-before關(guān)系指定的順序來執(zhí)行。如果重排序之后的執(zhí)行結(jié)果,與按happens-before關(guān)系來執(zhí)行的結(jié)果一致,那么JMM允許這種重排序。

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

區(qū)別:as-if-serial語義保障單線程內(nèi)程序的執(zhí)行結(jié)果不被改變,happens-before關(guān)系保證正確同步的多線程程序的執(zhí)行結(jié)果不被改變。

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:分配對(duì)象的內(nèi)存空間。
線程A-A2:設(shè)置instance指向內(nèi)存空間。
線程B-B1:判斷instance是否為空。
線程B-B2:由于instance不為null,線程B將訪問instance引用的對(duì)象。
線程A-A3:初始化對(duì)象
線程A-A4:訪問instance引用的對(duì)象。

存在的問題:
A2和A3重排序,線程B訪問到一個(gè)還沒初始化的對(duì)象。

解決方案:

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

9.生產(chǎn)者-消費(fèi)者模式


(二).線程

1.什么是線程?

現(xiàn)代操作系統(tǒng)在運(yùn)行一個(gè)程序時(shí),會(huì)為其創(chuàng)建一個(gè)進(jìn)程。現(xiàn)代操作系統(tǒng)調(diào)度的最小單元是線程,也叫輕量級(jí)進(jìn)程。在一個(gè)進(jìn)程里可以創(chuàng)建多個(gè)線程,這些線程都擁有各自的計(jì)數(shù)器,堆棧和局部變量等屬性。

2.創(chuàng)建線程的三種方式

(1).Thread

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

(2).Runnable

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

(3).Callable

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

3.Daemon線程

  • Daemon線程是一種支持型線程,因?yàn)樗饕挥米鞒绦蛑泻笈_(tái)調(diào)度以及支持性工作。
  • 當(dāng)一個(gè)Java虛擬機(jī)中不存在非Daemon線程的時(shí)候,Java虛擬機(jī)將會(huì)退出。
  • 可以通過調(diào)用Thread,setDaemon(true)將線程設(shè)置為Daemon線程。

4.等待/通知機(jī)制

等待/通知機(jī)制,是指一個(gè)線程A調(diào)用了對(duì)象O的wait()方法進(jìn)入等待狀態(tài),而另一個(gè)線程B調(diào)用了對(duì)象O的notify()或者notifyAll()方法,線程A收到通知后從對(duì)象O的wait()方法返回,進(jìn)而執(zhí)行后續(xù)操作。

等待方遵循如下規(guī)則:

  • 獲取對(duì)象的鎖
  • 如果條件不滿足,那么調(diào)用對(duì)象的wait()方法,被通知后仍要檢查條件。
  • 條件滿足則執(zhí)行對(duì)應(yīng)的邏輯。
while(條件不滿足){對(duì)象.wait(); }//處理對(duì)應(yīng)的邏輯

通知方遵循如下規(guī)則:

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

5.Thread.join()

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

6.ThreadLocal

ThreadLocal,即線程變量,是一個(gè)以ThreadLocal對(duì)象為鍵,任意對(duì)象為值的存儲(chǔ)結(jié)構(gòu)。這個(gè)結(jié)構(gòu)被附帶在線程上,也就是說一個(gè)線程可以根據(jù)一個(gè)ThreadLocal對(duì)象查詢到綁定到這個(gè)線程上的一個(gè)值。

7.線程的終止、中斷

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

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

(2).Thread.interrupted:測(cè)試當(dāng)前線程是否被中斷。

清除線程的中斷狀態(tài)。如果連續(xù)調(diào)用兩次這個(gè)方法,第二次調(diào)用會(huì)返回false(除非當(dāng)前線程再次被中斷,在第一次調(diào)用清除它的中斷狀態(tài)之后,并且在第二次調(diào)用檢查它之前)。

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

中斷狀態(tài)是否被重置取決于傳入的值。


(三).鎖

鎖是Java并發(fā)編程中最重要的同步機(jī)制。鎖除了讓臨界區(qū)互斥執(zhí)行外,還可以讓釋放鎖的線程向獲取同一個(gè)鎖的線程發(fā)送消息。

1.鎖的內(nèi)存語義:

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

2.重入鎖

(1).什么是重入鎖?
支持重進(jìn)入的鎖,表示鎖能夠支持一個(gè)線程對(duì)資源的重復(fù)加鎖。重入鎖支持獲取鎖時(shí)的公平性和非公平性選擇。

(2).解決兩個(gè)問題:

  • 線程再次獲取鎖:鎖需要去識(shí)別獲取鎖的線程是否為當(dāng)前占據(jù)鎖的線程,如果是,則再次獲取鎖。
  • 鎖的最終釋放:鎖的最終釋放要求鎖對(duì)于鎖獲取進(jìn)行計(jì)數(shù)自增,計(jì)數(shù)表示當(dāng)前鎖被重復(fù)獲取的次數(shù),而鎖被釋放時(shí),計(jì)數(shù)自減,當(dāng)計(jì)數(shù)等于0時(shí)表示鎖已經(jīng)釋放。

3.排他鎖:ReentrantLock

(1)公平鎖:

  • 公平鎖釋放時(shí),最后要寫一個(gè)volatile變量state。
  • 公平鎖獲取時(shí),首先會(huì)去讀volatile變量。

(2)非公平鎖:

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

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

公平性與否是針對(duì)獲取鎖而言的。

  • 公平鎖:如果一個(gè)鎖是公平的,那么獲取鎖的順序就應(yīng)該符合請(qǐng)求的絕對(duì)時(shí)間順序,也就是FIFO。
  • 非公平鎖:剛釋放鎖的線程再次獲取同步狀態(tài)的幾率會(huì)非常大,使得其他線程只能在同步隊(duì)列中等待。

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

4.Lock

(1)讀寫鎖:ReentrantReadWriteLock

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

讀寫鎖的實(shí)現(xiàn)分析:

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

鎖的四種狀態(tài):無鎖,偏向鎖,輕量級(jí)鎖,重量級(jí)鎖

5.LockSupport工具

當(dāng)需要阻塞或喚醒一個(gè)線程的時(shí)候,都會(huì)使用LockSupport工具類來完成相應(yīng)的工作。

6.Condition接口

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

Condition的實(shí)現(xiàn):等待隊(duì)列,等待和通知。

  • 等待隊(duì)列:等待隊(duì)列是一個(gè)FIFO隊(duì)列,在隊(duì)列中的每一個(gè)節(jié)點(diǎn)都包含了一個(gè)線程引用,該線程是在Condition對(duì)象上等待的線程,如果一個(gè)線程調(diào)用了Condition.await()方法,那么該線程會(huì)釋放鎖,構(gòu)造成節(jié)點(diǎn)加入等待隊(duì)列并進(jìn)入等待狀態(tài)。
  • 等待:調(diào)用Condition的await()方法(或者以await開頭的方法),會(huì)使當(dāng)前線程進(jìn)入等待隊(duì)列并釋放鎖,同時(shí)線程狀態(tài)變?yōu)榈却隣顟B(tài)。當(dāng)從await()方法返回時(shí),當(dāng)前線程一定獲取了Condition相關(guān)的鎖。
  • 通知:調(diào)用Condition的signal()方法,將會(huì)喚醒在等待隊(duì)列中等待時(shí)間最長的節(jié)點(diǎn)(首節(jié)點(diǎn)),在喚醒節(jié)點(diǎn)之前,會(huì)將節(jié)點(diǎn)移步到同步隊(duì)列。

7.避免活躍性危險(xiǎn)

(1).死鎖

  • 哲學(xué)家用餐問題:每個(gè)線程都擁有別的線程需要的資源,同時(shí)又等待別人擁有的資源,在獲得別的資源之前不會(huì)釋放自己手上的資源。
  • 數(shù)據(jù)庫事務(wù)死鎖:數(shù)據(jù)庫如果發(fā)生死鎖,會(huì)選擇一個(gè)事務(wù)釋放資源。
  • 鎖順序死鎖:線程A,B都需要鎖1,2。線程A先獲得鎖1 ,再請(qǐng)求鎖2 ,線程B先獲得鎖2,再請(qǐng)求鎖1 。

8.死鎖的避免與診斷

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

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

(3).線程轉(zhuǎn)儲(chǔ)

避免死鎖的四種方式:

  • 避免一個(gè)線程同時(shí)獲得多個(gè)鎖。
  • 避免一個(gè)線程在鎖內(nèi)同時(shí)占用多個(gè)資源,盡量保證每個(gè)鎖只獲得一個(gè)資源。
  • 使用定時(shí)鎖。
  • 對(duì)于數(shù)據(jù)庫鎖,加鎖和解鎖必須在一個(gè)數(shù)據(jù)庫連接里,否則會(huì)出現(xiàn)解鎖失敗的情況。

9.饑餓,糟糕的響應(yīng)性,活鎖

  • 饑餓:線程由于無法訪問它需要的資源而不能繼續(xù)執(zhí)行,引發(fā)饑餓最常見的資源是CPU時(shí)鐘周期。
  • 活鎖通常發(fā)送在處理事務(wù)消息的應(yīng)用程序中,如果不能成功地處理事務(wù)消息,那么消息機(jī)制將回滾整個(gè)事務(wù),將這個(gè)事務(wù)放在等待隊(duì)列的開頭。
  • 當(dāng)多個(gè)相互協(xié)作的線程都對(duì)彼此響應(yīng)從而修改各自的狀態(tài),并使得任何一個(gè)線程都無法繼續(xù)執(zhí)行時(shí),就發(fā)生了活鎖。
  • 在并發(fā)應(yīng)用中,通過等待隨機(jī)長度的時(shí)間和回退可以有效地避免活鎖的發(fā)生。

(四).同步器

(1).實(shí)現(xiàn)

  • 同步隊(duì)列:同步器依賴內(nèi)部的同步隊(duì)列(一個(gè)FIFO雙向隊(duì)列)來完成同步狀態(tài)的管理,當(dāng)前線程獲得同步狀態(tài)失敗時(shí),同步器會(huì)將當(dāng)前線程以及等待狀態(tài)等信息構(gòu)造成為一個(gè)節(jié)點(diǎn)并將其加入同步隊(duì)列,同時(shí)會(huì)阻塞當(dāng)前線程,當(dāng)同步狀態(tài)釋放時(shí),會(huì)把首節(jié)點(diǎn)中的線程喚醒,使其再次嘗試獲取同步狀態(tài)。
  • 獨(dú)占式同步狀態(tài)獲取與釋放:在獲取同步狀態(tài)時(shí),同步器維護(hù)一個(gè)隊(duì)列,獲取狀態(tài)失敗的線程會(huì)被加入隊(duì)列中并在隊(duì)列中進(jìn)行自旋,移除隊(duì)列的原因時(shí)自旋獲取了同步狀態(tài)且前驅(qū)節(jié)點(diǎn)時(shí)頭節(jié)點(diǎn)。在釋放同步狀態(tài)時(shí),同步器調(diào)用tryRelease(int arg)方法釋放同步狀態(tài),然后喚醒頭節(jié)點(diǎn)的后繼結(jié)點(diǎn)。
  • 共享式同步狀態(tài)獲取與釋放:共享式獲取與獨(dú)占式獲取最主要的區(qū)別在于同一時(shí)刻能否有多個(gè)線程同時(shí)獲取同步狀態(tài)。
  • 獨(dú)占式超時(shí)獲取同步狀態(tài):在指定的時(shí)間段內(nèi)獲取同步狀態(tài),如果獲取到同步狀態(tài)返回true,否則,返回false。

(2).AbstractQueuedSynchronized

用來構(gòu)建鎖或者其他同步組件的基礎(chǔ)框架,使用了一個(gè)int成員變量表示同步狀態(tài),通過內(nèi)置的FIFO隊(duì)列來完成資源獲取線程的排隊(duì)工作。

提供了三個(gè)對(duì)同步狀態(tài)進(jìn)行修改的方法:

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

(五).并發(fā)容器和框架

(1).ConcurrentHashMap

與HashMap,HashTable對(duì)比:

  • HashMap是線程不安全,且可能導(dǎo)致程序死循環(huán)。
  • HashTable效率低下。
  • ConcurrentHashMap的鎖分段技術(shù)可有效提升訪問效率。首先將數(shù)據(jù)分成一段一段的存儲(chǔ),然后給每一段數(shù)據(jù)配一把鎖,當(dāng)一個(gè)線程占用鎖訪問其中一個(gè)段的數(shù)據(jù)的時(shí)候,其他段的數(shù)據(jù)也能被其他線程訪問。

ConCurrentHashMap的結(jié)構(gòu):ConCurrentHashMap是由Segment數(shù)組結(jié)構(gòu)和HashEntry數(shù)組結(jié)構(gòu)組成。

(2).ConcurrentLinkedQueue

ConcurrentLinkedQueue由head節(jié)點(diǎn)和tail節(jié)點(diǎn)組成,每個(gè)節(jié)點(diǎn)(Node)由節(jié)點(diǎn)元素(item)和指向下一個(gè)節(jié)點(diǎn)(next)的引用組成,從而組成一張鏈表結(jié)構(gòu)的隊(duì)列。

(3).阻塞隊(duì)列

  • 插入:當(dāng)隊(duì)列滿時(shí),隊(duì)列會(huì)堵塞插入元素的線程,直到隊(duì)列不滿。
  • 移除:當(dāng)隊(duì)列為空,獲取元素的線程會(huì)等待線程為非空。

實(shí)現(xiàn)原理:
使用通知模式實(shí)現(xiàn)。當(dāng)生產(chǎn)者往滿的隊(duì)列里添加元素時(shí)會(huì)堵塞生產(chǎn)者,當(dāng)消費(fèi)者消費(fèi)了一個(gè)隊(duì)列中的元素后,會(huì)通知生產(chǎn)者當(dāng)前隊(duì)列可用。

1.ArrayBlockingQueue

  • 數(shù)組實(shí)現(xiàn)的有界阻塞隊(duì)列,按照先進(jìn)先出的原則對(duì)元素進(jìn)行排序。

2.LinkedBlockingQueue

  • 繼承了AbstractQueue類,實(shí)現(xiàn)了BlockingQueue接口。
  • 采用先進(jìn)先出的排列方式,頭結(jié)點(diǎn)是入隊(duì)時(shí)間最長的元素,尾結(jié)點(diǎn)是入隊(duì)時(shí)間最短的元素。新結(jié)點(diǎn)添加到隊(duì)尾,從隊(duì)頭彈出結(jié)點(diǎn)。
  • 鏈表隊(duì)列的特點(diǎn)是:跟基于數(shù)組的隊(duì)列相比有更大的吞吐量,但在大多并發(fā)應(yīng)用中性能會(huì)比較差。
  • LinkedBlockingQueue可以在創(chuàng)建的時(shí)候傳遞一個(gè)容量參數(shù),限制隊(duì)列的長度,不設(shè)定的情況下,默認(rèn)是Integer.MAX_VALUE。在沒有超過隊(duì)列邊界的情況下,每次添加會(huì)自動(dòng)創(chuàng)建鏈表結(jié)點(diǎn)。

3.PriorityBlockingQueue

  • 是一個(gè)支持優(yōu)先級(jí)的無界阻塞隊(duì)列。默認(rèn)情況下時(shí)自然順序升序排序。

4.SychronousQueue

  • SynchronousQueue是一個(gè)不存儲(chǔ)元素的堵塞隊(duì)列,每一個(gè)put操作必須等待一個(gè)take操作,否則不能繼續(xù)添加元素。

5.DelayQueue

  • 延遲隊(duì)列:無界隊(duì)列,只有延遲過期的任務(wù)才能加入隊(duì)列。隊(duì)列的隊(duì)首元素是在過去延遲過期最長的元素。如果沒有延遲到期,隊(duì)列中就沒有元素,調(diào)用poll方法會(huì)返回null。當(dāng)調(diào)用元素的getDelay(TimeUnit.NANOSECONDS)方法返回等于或小于0的值時(shí),出現(xiàn)到期。
  • DelayQueue的應(yīng)用場(chǎng)景:a.緩存系統(tǒng):把需要緩存的元素加入DelayQueue中,讓一個(gè)線程循環(huán)測(cè)試是否能從DelayQueue中獲取元素,能表示緩存到期。b.定時(shí)任務(wù)調(diào)度。
  • Timer和DelayQueue的區(qū)別?Timer只能處理單個(gè)后臺(tái)線程,而DelayQueue可以處理多個(gè)。

6 . LinkedTransferQueue

  • LinkedTransferQueue是一個(gè)由鏈表結(jié)構(gòu)組成的無界阻塞TransferQueue隊(duì)列。

7 . LinkedBlockingDeque

  • 一個(gè)由鏈表結(jié)構(gòu)組成的雙向阻塞隊(duì)列,可以運(yùn)用在“工作竊取”模式中。

(4).Fork/Join框架

用于并行執(zhí)行任務(wù),把一個(gè)大任務(wù)分割成小任務(wù),再把每個(gè)小任務(wù)的結(jié)果匯總成大任務(wù)結(jié)果。Fork是把一個(gè)大任務(wù)切分成若干子任務(wù)并行執(zhí)行,Join是合并這些子任務(wù)的執(zhí)行結(jié)果。

(5).工作竊取算法

指某個(gè)線程從其他隊(duì)列里竊取任務(wù)來執(zhí)行。為了減少竊取任務(wù)線程和別竊取任務(wù)線程之間的競(jìng)爭,使用雙端隊(duì)列,被竊取任務(wù)線程從雙端隊(duì)列的頭部拿任務(wù)執(zhí)行,竊取任務(wù)線程從雙端隊(duì)列的尾部拿任務(wù)執(zhí)行。

  • 工作竊取算法的優(yōu)點(diǎn):充分利用線程進(jìn)行并行計(jì)算,減少線程間的競(jìng)爭。
  • 工作竊取算法的缺點(diǎn):在某些情況下還是存在競(jìng)爭,比如雙端隊(duì)列里只有一個(gè)任務(wù)時(shí),并且該算法會(huì)消耗更多的系統(tǒng)資源,比如創(chuàng)建多個(gè)線程和多個(gè)雙端隊(duì)列。

(六).Java并發(fā)工具類

1.CyclicBarrier

一組線程在到達(dá)一個(gè)屏障(同步點(diǎn))前被堵塞,直到最后一個(gè)線程到達(dá)屏障時(shí),屏障才會(huì)放行,這組線程才能繼續(xù)執(zhí)行。

應(yīng)用場(chǎng)景:可以用于多線程計(jì)算數(shù)據(jù),最后合并計(jì)算結(jié)果。

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

2.Semaphore

用來控制同時(shí)訪問特定資源的線程數(shù)量,通過協(xié)調(diào)各個(gè)線程,以保證合理的使用公共資源。

應(yīng)用場(chǎng)景:可以用于流量控制,特別是公共資源有限的應(yīng)用場(chǎng)景,比如數(shù)據(jù)庫連接。

3.CountDownLatch

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

4.Exchanger

Exchanger是一個(gè)用于線程間協(xié)作的工具類。Exchanger用于進(jìn)行線程間的數(shù)據(jù)交換。它提供一個(gè)同步點(diǎn),在這個(gè)同步點(diǎn),兩個(gè)線程可以交換彼此的數(shù)據(jù)。這兩個(gè)線程通過exchange方法交換數(shù)據(jù),如果第一個(gè)線程先執(zhí)行exchange()方法,會(huì)一直等待第二個(gè)線程也執(zhí)行exchange()方法,當(dāng)兩個(gè)線程都到達(dá)同步點(diǎn)時(shí),這兩個(gè)線程就可以交換數(shù)據(jù),將本線程生產(chǎn)出來的數(shù)據(jù)傳遞給對(duì)方。

應(yīng)用場(chǎng)景:用于遺傳算法。


(七).原子操作類

1.原子更新基本類型類

  • AtomicBoolean
  • AtomicInteger
  • AtomicLong

2.原子更新數(shù)組

  • AtomicIntegerArray
  • AtomicLongArray
  • AtomicReferenceArray

3.原子更新引用類型

  • AtomicReference
  • AtomicReferenceFieldUpdater 原子更新引用類型里的字段
  • AtomicMarkableReference 原子更新帶有標(biāo)記位的引用類型。

4.原子更新字段類

  • AtomicIntegerFieldUpdater 原子更新整型的字段的更新器
  • AtomicLongFieldUpdater 原子更新長整型字段的更新器
  • AtomicStampedReference 原子更新帶有版本號(hào)的引用類型。該類將整數(shù)值與引用關(guān)聯(lián)起來,可用于原子的更新數(shù)據(jù)和數(shù)據(jù)的版本號(hào),可以解決使用CAS進(jìn)行原子更新時(shí)可能出現(xiàn)的ABA問題。

(八).Executor框架(執(zhí)行機(jī)制)

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

1.異步計(jì)算的結(jié)果:FutureTask和Future

2.任務(wù)執(zhí)行

(1).Executor(核心接口)

Executor的生命周期:創(chuàng)建,提交,開始,完成

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

  • ExecutorService的生命周期:運(yùn)行,關(guān)閉,已終止
  • ExecutorService.shutDown和ExecutorService.shutDownNow的區(qū)別
調(diào)用的方法作用
shutDown不再允許新的任務(wù)添加到等待隊(duì)列,正在執(zhí)行的任務(wù)和在等待隊(duì)列中的任務(wù)會(huì)執(zhí)行完畢再關(guān)閉。
shurDownNow立刻關(guān)閉。需要知道正在執(zhí)行但是還沒執(zhí)行完畢的任務(wù)。
  • ExecutorService.submit()和ExecutorService.execute()的區(qū)別:接口ExecutorService的execute()方法是繼承自Executor。
調(diào)用的方法作用
execute用于提交不需要返回值的任務(wù),所以無法判斷任務(wù)是否被線程池執(zhí)行成功。
submit用于提交需要返回值的任務(wù)。線程池會(huì)返回一個(gè)Future類型的對(duì)象,通過這個(gè)Future對(duì)象可以判斷任務(wù)是否執(zhí)行成功,并且通過Future的get()方法來獲取返回值,get()方法會(huì)阻塞線程直到任務(wù)完成。
  • ExecutorService的創(chuàng)建:
調(diào)用分類使用
Executors.newSingleThreadExecutor()ThreadPoolExecutor應(yīng)用場(chǎng)景:適用于需要保證順序地執(zhí)行各個(gè)任務(wù);并且在任意時(shí)間點(diǎn),不會(huì)有多個(gè)線程活動(dòng)的應(yīng)用場(chǎng)景。
Exectors.newFiexedThreadPool()ThreadPoolExecutor1.創(chuàng)建一個(gè)線程池,具有固定線程數(shù),運(yùn)行在共享的無界隊(duì)列中。2.在大多數(shù)時(shí)候,線程會(huì)主動(dòng)執(zhí)行任務(wù),當(dāng)所有的線程都在執(zhí)行任務(wù)時(shí),有新的任務(wù)加入進(jìn)來,就會(huì)進(jìn)入等待隊(duì)列(可以有源源不斷的任務(wù)加入進(jìn)來,因?yàn)槭菬o界隊(duì)列),當(dāng)有空閑的線程,等待隊(duì)列中的任務(wù)就會(huì)被執(zhí)行。3.如果有線程在執(zhí)行過程中因?yàn)閳?zhí)行失敗要關(guān)閉,新創(chuàng)建的線程會(huì)替失敗的線程執(zhí)行接下來的任務(wù)。4.如果想要關(guān)閉這個(gè)線程池,可以調(diào)用ExecutorService的shutDown方法。應(yīng)用場(chǎng)景:適用于為了滿足資源管理的需求,而需要限制當(dāng)前線程數(shù)量的應(yīng)用場(chǎng)景,它適用于負(fù)載比較重的服務(wù)器。
Executors.newCachedThreadPool()ThreadPoolExecutor應(yīng)用場(chǎng)景:適用于執(zhí)行很多短期異步任務(wù)的小程序,或者是負(fù)載較輕的服務(wù)器。
Executors.newScheduledThreadPool()ScheduledThreadPoolExecutor應(yīng)用場(chǎng)景:適用于需要多個(gè)后臺(tái)線程執(zhí)行周期任務(wù),同時(shí)為了滿足管理的需求而需要限制后臺(tái)線程的數(shù)量的應(yīng)用場(chǎng)景。
Executors.newSingleThreadScheduledExecutor()ScheduledThreadPoolExecutor應(yīng)用場(chǎng)景:需要單個(gè)后臺(tái)線程執(zhí)行周期任務(wù),同時(shí)需要保證順序地執(zhí)行各個(gè)任務(wù)。

3.任務(wù):Runnable接口和Callable接口


(九).其他

1.jstack

打開cmd,輸入:

  • jps 查看pid(進(jìn)程號(hào))
  • jstack pid

2.資源限制:指在進(jìn)行并發(fā)編程時(shí),程序的執(zhí)行速度受到計(jì)算機(jī)硬件資源或軟件資源的限制。

3.上下文切換

減少上下文切換的方法:

  • 無鎖并發(fā)編程
  • CAS算法
  • 使用最少線程
  • 使用協(xié)程

總結(jié)

以上是生活随笔為你收集整理的Java并发的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。