真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Java并發(fā)編程-創(chuàng)新互聯(lián)

文章目錄
        • 線程的一些概念
        • 實現(xiàn)多線程的方法
        • 線程同步和死鎖
        • 生產(chǎn)者和消費者模型-線程通信
        • 線程池
        • 并發(fā)編程的3個基本概念
        • volatile、ThreadLocal的使用場景和原理
          • volatile 原理
          • synchronized 和 volatile 區(qū)別
          • ThreadLocal 原理
          • ThreadLocal 源碼分析
            • ThreadLocalMap底層結(jié)構(gòu)是怎么樣子的呢?
            • 為什么需要數(shù)組呢?沒有了鏈表怎么解決Hash沖突呢?
            • ThreadLocal 存在什么安全問題?
          • ThreadLocal 源碼總結(jié)
          • synchronized

創(chuàng)新互聯(lián)主營青岡網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,APP應(yīng)用開發(fā),青岡h5微信平臺小程序開發(fā)搭建,青岡網(wǎng)站營銷推廣歡迎青岡等地區(qū)企業(yè)咨詢線程的一些概念

線程,程序、進(jìn)程的基本概念

線程與進(jìn)程相似,但線程是一個比進(jìn)程更小的執(zhí)行單位。一個進(jìn)程在其執(zhí)行的過程中可以產(chǎn)生多個線程。與進(jìn)程不同的是同類的多個線程共享同一塊內(nèi)存空間和一組系統(tǒng)資源,所以系統(tǒng)在產(chǎn)生一個線程,或是在各個線程之間作切換工作時,負(fù)擔(dān)要比進(jìn)程小得多,也正因為如此,線程也被稱為輕量級進(jìn)程。

程序是含有指令和數(shù)據(jù)的文件,被存儲在磁盤或其他的數(shù)據(jù)存儲設(shè)備中,也就是說程序是靜態(tài)的代碼。

進(jìn)程是程序的一次執(zhí)行過程,是系統(tǒng)運行程序的基本單位,因此進(jìn)程是動態(tài)的。系統(tǒng)運行一個程序即是一個進(jìn)程從創(chuàng)建,運行到消亡的過程。簡單來說,一個進(jìn)程就是一個執(zhí)行中的程序,它在計算機中一個指令接著一個指令地執(zhí)行著,同時,每個進(jìn)程還占有某些系統(tǒng)資源如 CPU 時間,內(nèi)存空間,文件,文件,輸入輸出設(shè)備的使用權(quán)等等。換句話說,當(dāng)程序在執(zhí)行時,將會被操作系統(tǒng)載入內(nèi)存中。 線程是進(jìn)程劃分成的更小的運行單位。

線程和進(jìn)程大的不同在于基本上各進(jìn)程是獨立的,而各線程則不一定,因為同一進(jìn)程中的線程極有可能會相互影響。從另一角度來說,進(jìn)程屬于操作系統(tǒng)的范疇,主要是同一段時間內(nèi),可以同時執(zhí)行一個以上的程序,而線程則是在同一程序內(nèi)幾乎同時執(zhí)行一個以上的程序段。

線程有哪些狀態(tài)
在這里插入圖片描述
形成死鎖的四個必要條件
在這里插入圖片描述
說線程安全問題:什么是線程安全,如何實現(xiàn)線程安全

線程安全 - 如果線程執(zhí)行過程中不會產(chǎn)生共享資源的沖突,則線程安全。

實現(xiàn)線程安全的三種方式:

  1. 互斥同步
    臨界區(qū)(悲觀鎖)):syncronized、ReentrantLock
    信號量:semaphore
    互斥量:mutex
  2. 非阻塞同步:CAS(Compare And Swap)

JUC中提供了幾個 Automic 類以及每個類上的原子操作就是樂觀鎖機制。不激烈情況下,性能比synchronized略遜,而激烈的時候,也能維持常態(tài)。激烈的時候,Atomic 的性能會優(yōu)于 ReentrantLock 一倍左右。但是其有一個缺點,就是只能同步一個值,一段代碼中只能出現(xiàn)一個 Atomic 的變量,多于一個同步無效。因為他不能在多個 Atomic 之間同步。
非阻塞鎖是不可重入的,否則會造成死鎖。

  1. 無同步方案

可重入代碼 使用Threadlocal 類來包裝共享變量 或者 volatile 關(guān)鍵字修飾共享變量,做到每個線程有自己的copy 線程本地存儲

實現(xiàn)多線程的方法

繼承 Thread 類

實現(xiàn) Runnabel 接口

實現(xiàn) Callable 接口

線程同步和死鎖

手寫一段死鎖的代碼,注意static關(guān)鍵字的用法,保證資源的唯一
在這里插入圖片描述

生產(chǎn)者和消費者模型-線程通信

代碼參考我的另一篇博客

線程同步 和 線程通信 是兩個概念,并且 synchronized 只能用于線程同步,不能用于線程通信(體現(xiàn)在代碼中的現(xiàn)象是,雖然線程是同步執(zhí)行的,但是會出現(xiàn)其中一個線程重復(fù)拿到時間片的現(xiàn)象)。線程通信 可以結(jié)合 synchronized + 信號燈法使用。

面試題: 為什么線程通信方法wait(),notify(),notifyAll()要被定義到Object類中?

Java中任何對象都可以被當(dāng)作鎖對象,調(diào)用wait方法,那么線程便會處于該對象的等待池中,調(diào)用notify(),notifyAll()方法,用于喚醒線程去獲取對象的鎖。Java中沒有提供任何對象使用的鎖,但是任何對象都繼承于Object類,所以定義在Object類中最合適。

線程池

線程池的優(yōu)點

  • 線程是稀缺資源,使用線程池可以減少創(chuàng)建和銷毀線程的次數(shù),每個工作線程都可以重復(fù)使用。
  • 可以根據(jù)系統(tǒng)的承受能力,調(diào)整線程池中工作線程的數(shù)量,防止因為消耗過多內(nèi)存導(dǎo)致服務(wù)器崩潰。

線程池的創(chuàng)建

public ThreadPoolExecutor(int corePoolSize,
						  int maximumPoolSize,
						  long keepAliveTime, 
						  TimeUnit unit, 
						  BlockingQueueworkQueue,
						  RejectedExecutionHandler handler)

corePoolSize: 線程池核心線程數(shù)量

maximumPoolSize: 線程池大線程數(shù)量

核心線程數(shù)和大線程數(shù)動畫理解

keepAliverTime: 當(dāng)活躍線程數(shù)大于核心線程數(shù)時,空閑的多余線程大存活時間

unit: 存活時間的單位

workQueue: 存放任務(wù)的隊列

handler: 超出線程范圍和隊列容量的任務(wù)的處理程序

線程池的實現(xiàn)原理
提交一個任務(wù)到線程池中,線程池的處理流程如下:

  1. 判斷線程池里的核心線程是否都在執(zhí)行任務(wù),如果不是(核心線程空閑或者還有核心線程沒有被創(chuàng)建)則創(chuàng)建一個新的工作線程來執(zhí)行任務(wù)。如果核心線程都在執(zhí)行任務(wù),則進(jìn)入下個流程。
  2. 線程池判斷工作隊列是否已滿,如果工作隊列沒有滿,則將新提交的任務(wù)存儲在這個工作隊列里。如果工作隊列滿了,則進(jìn)入下個流程。
  3. 判斷線程池里的線程是否都處于工作狀態(tài),如果沒有,則創(chuàng)建一個新的工作線程來執(zhí)行任務(wù)。如果已經(jīng)滿了,則交給飽和策略來處理這個任務(wù)。

線程池的源碼解讀:

  1. ThreadPoolExecutor的execute()方法

從結(jié)果可以觀察出:

  1. 創(chuàng)建的線程池具體配置為:核心線程數(shù)量為5個;全部線程數(shù)量為10個;工作隊列的長度為5。
  2. 我們通過queue.size()的方法來獲取工作隊列中的任務(wù)數(shù)。
  3. 運行原理:剛開始都是在創(chuàng)建新的線程,達(dá)到核心線程數(shù)量5個后,新的任務(wù)進(jìn)來后不再創(chuàng)建新的線程,而是將任
    務(wù)加入工作隊列,任務(wù)隊列到達(dá)上線5個后,新的任務(wù)又會創(chuàng)建新的普通線程,直到達(dá)到線程池大的線
    程數(shù)量10個,后面的任務(wù)則根據(jù)配置的飽和策略來處理。我們這里沒有具體配置,使用的是默認(rèn)的配置
    AbortPolicy:直接拋出異常。

線程池中Callable異常處理分析
見參考

并發(fā)編程的3個基本概念

原子性
定義: 即一個操作或者多個操作 要么全部執(zhí)行并且執(zhí)行的過程不會被任何因素打斷,要么就都不執(zhí)行。Java中的原子性操作包括:

(1)基本類型的讀取和賦值操作,且賦值必須是值賦給變量,變量之間的相互賦值不是原子性操作。

(2)所有引用reference的賦值操作

(3)java.concurrent.Atomic.* 包中所有類的一切操作

可見性
定義: 指當(dāng)多個線程訪問同一個變量時,一個線程修改了這個變量的值,其他線程能夠立即看得到修改的值。

在多線程環(huán)境下,一個線程對共享變量的操作對其他線程是不可見的。Java提供了volatile來保證可見性,當(dāng)一個變量被volatile修飾后,表示著線程本地內(nèi)存無效,當(dāng)一個線程修改共享變量后他會立即被更新到主內(nèi)存中,其他線程讀取共享變量時,會直接從主內(nèi)存中讀取。

當(dāng)然,synchronize和Lock都可以保證可見性。synchronized和Lock能保證同一時刻只有一個線程獲取鎖然后執(zhí)行同步代碼,并且在釋放鎖之前會將對變量的修改刷新到主存當(dāng)中。因此可以保證可見性。

有序性
定義: 即程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。

Java內(nèi)存模型中的有序性可以總結(jié)為:如果在本線程內(nèi)觀察,所有操作都是有序的;如果在一個線程中觀察另一個線程,所有操作都是無序的。前半句是指 “線程內(nèi)表現(xiàn)為串行語義”,后半句是指"指令重排序"現(xiàn)象和"工作內(nèi)存主主內(nèi)存同步延遲"現(xiàn)象。

在Java內(nèi)存模型中,為了效率是允許編譯器和處理器對指令進(jìn)行重排序,當(dāng)然重排序不會影響單線程的運行結(jié)果,但是對多線程會有影響。Java提供volatile來保證一定的有序性。

補充:指令重排
見轉(zhuǎn)載

重排序是指編譯器和處理器為了優(yōu)化程序性能而對指令序列進(jìn)行排序的一種手段。重排序需要遵守一定規(guī)則:

  1. 重排序操作不會對存在數(shù)據(jù)依賴關(guān)系的操作進(jìn)行重排序

比如:a=1; b=a; 這個指令序列,由于第二個操作依賴于第一個操作,所以在編譯時和處理器運行時不會被重排序這兩個操作。

  1. 重排序是為了優(yōu)化性能,但是不管怎么重排序,單線程下程序的執(zhí)行結(jié)果不能被改變

比如:a=1; b=2; c=a+b; 這三個操作,第一步 a=1; 和第二步 b=2; 由于不存在數(shù)據(jù)依賴關(guān)系, 所以可能會發(fā)生重排序,但是 c=a+b 這個操作是不會被重排序的,因為需要保證最終的結(jié)果一定是 c=a+b=3

重排序在單線程下一定能保證結(jié)果的正確性,但是在多線程環(huán)境下,可能發(fā)生重排序,影響結(jié)果。

下例中的 1 和 2 由于不存在數(shù)據(jù)依賴關(guān)系,則有可能會被重排序,先執(zhí)行status=true再執(zhí)行a=2。而此時線程B會順利到達(dá)4處,而線程A中 a=2 這個操作還未被執(zhí)行,所以 b=a+1 的結(jié)果也有可能依然等于2。

public class TestVolatile {int a = 1;
	boolean status = false;//狀態(tài)切換為true 
	public void changeStatus {a = 2;   					//1
		status = true;  			//2
	}
	//若狀態(tài)為true,則為running
	public void run() {if(status) {		//3
			int b = a + 1; 			//4
			System.out.println(b);
		}
	}
}
volatile、ThreadLocal的使用場景和原理 volatile 原理

volatile關(guān)鍵字最全總結(jié)

(1)volatile 變量進(jìn)行寫操作時,JVM 會向處理器發(fā)送一條 Lock 前綴的指令,將這個變量所在緩存行的數(shù)據(jù)寫會到系統(tǒng)內(nèi)存。
Lock 前綴指令實際上相當(dāng)于一個內(nèi)存屏障(也成內(nèi)存柵欄),它確保指令重排序時不會把其后面的指令排到內(nèi)存屏障之前的位置,也不會把前面的指令排到內(nèi)存屏障的后面;即在執(zhí)行到內(nèi)存屏障這句指令時,在它前面的操作已經(jīng)全部完成。

(2)它會強制將對緩存的修改操作立即寫入主存;

(3)如果是寫操作,它會導(dǎo)致其他CPU中對應(yīng)的緩存行無效

volatile 保證可見性、有序性,不保證原子性, 所以 volatile 不適合復(fù)合操作

例如,inc++ 不是一個原子性操作,可以由讀取、加、賦值3步組成,所以結(jié)果并不能達(dá)到30000。


分析:

開啟10個線程,每個線程都自加1000次,如果不出現(xiàn)線程安全的問題最終的結(jié)果應(yīng)該就是:10*1000= 10000;可是運行多次都是小于10000的結(jié)果,問題在于 volatile 并不能保證原子性,在前面說過 counter++ 這并不是一個原子操作,包含了三個步驟:1.讀取變量inc的值;2.對inc加一;3.將新值賦值給變量inc。如果線程 A 讀取 inc 到工作內(nèi)存后,其他線程對這個值已經(jīng)做了自增操作后,那么線程A的這個值自然而然就是一個過期的值,因此,總結(jié)果必然會是小于100000的。

如果讓volatile保證原子性,必須符合以下兩條規(guī)則:

  1. 運算結(jié)果并不依賴于變量的當(dāng)前值,或者能夠確保只有一個線程修改變量的值;
  2. 變量不需要與其他的狀態(tài)變量共同參與不變約束

解決方法:

  1. 采用synchronized
  2. 采用Lock
  3. 采用java并發(fā)包中的原子操作類,原子操作類是通過CAS循環(huán)的方式來保證其原子性的

volatile的適用場景:

  1. 狀態(tài)標(biāo)志,如:初始化或請求停機
  2. 一次性安全發(fā)布,如:單列模式
  3. 獨立觀察,如:定期更新某個值
  4. “volatile bean” 模式
  5. 開銷較低的“讀-寫鎖”策略,如:計數(shù)器
synchronized 和 volatile 區(qū)別
  1. volatile主要應(yīng)用在多個線程對實例變量更改的場合,刷新主內(nèi)存共享變量的值從而使得各個線程可以獲得最新的值,線程讀取變量的值需要從主存中讀取;synchronized則是鎖定當(dāng)前變量,只有當(dāng)前線程可以訪問該變量,其他線程被阻塞住。另外,synchronized還會創(chuàng)建一個內(nèi)存屏障,內(nèi)存屏障指令保證了所有CPU操作結(jié)果都會直接刷到主存中(即釋放鎖前),從而保證了操作的內(nèi)存可見性,同時也使得先獲得這個鎖的線程的所有操作
  2. volatile僅能使用在變量級別;synchronized則可以使用在變量、方法、和類級別的。
  3. volatile不會造成線程的阻塞;synchronized可能會造成線程的阻塞,比如多個線程爭搶 synchronized 鎖對象時,會出現(xiàn)阻塞。
  4. volatile 僅能實現(xiàn)變量的修改可見性,不能保證原子性;而synchronized 則可以保證變量的修改可見性和原子性,因為線程獲得鎖才能進(jìn)入臨界區(qū),從而保證臨界區(qū)中的所有語句全部得到執(zhí)行。
  5. volatile 標(biāo)記的變量不會被編譯器優(yōu)化,可以禁止進(jìn)行指令重排;synchronized標(biāo)記的變量可以被編譯器優(yōu)化。
ThreadLocal 原理

ThreadLocal 是用來維護(hù)本線程的變量的,并不能解決共享變量的并發(fā)問題。
ThreadLocal 是各線程將值存入該線程的map中,以 ThreadLocal 自身作為key,需要用時獲得的是該線程之前存入的值。如果存入的是共享變量,那取出的也是共享變量,并發(fā)問題還是存在的。

ThreadLocal的適用場景:

  1. 數(shù)據(jù)庫連接
  2. Session管理
ThreadLocal 源碼分析

參考三太子敖丙——ThreadLocal

Thread 類中維護(hù)了成員變量 threadLocals,類型是ThreadLocalMap。而 ThreadLocalMap 又是 ThreadLocal 類中的 內(nèi)部類。

每一個線程維護(hù)自己的 threadLocals 成員變量,及 ThreadLocalMap 類型的 map 集合用來存放 ThreadLocal類型的對象。
在這里插入圖片描述
在這里插入圖片描述

ThreadLocalMap底層結(jié)構(gòu)是怎么樣子的呢? 為什么需要數(shù)組呢?沒有了鏈表怎么解決Hash沖突呢? ThreadLocal 存在什么安全問題?

ThreadLocal 涉及到的兩個層面的內(nèi)存自動回收
1)在 ThreadLocal 層面的內(nèi)存回收

當(dāng)線程死亡時,那么所有的保存在的線程局部變量就會被回收,其實這里是指線程Thread對象中的 ThreadLocal.ThreadLocalMap threadLocals 會被回收,這是顯然的。

2)ThreadLocalMap 層面的內(nèi)存回收

如果線程可以活很長的時間,并且該線程保存的線程局部變量有很多(也就是 Entry 對象很多),那么就涉及到在線程的生命期內(nèi)如何回收 ThreadLocalMap 的內(nèi)存了,不然的話,Entry對象越多,那么ThreadLocalMap 就會越來越大,占用的內(nèi)存就會越來越多,所以對于已經(jīng)不需要了的線程局部變量,就應(yīng)該清理掉其對應(yīng)的Entry對象。

使用的方式是,Entry對象的 key 是WeakReference 的包裝,當(dāng)ThreadLocalMap 的 private Entry[] table ,已經(jīng)被占用達(dá)到了三分之二時 threshold = 2/3 (也就是線程擁有的局部變量超過了10個) ,就會嘗試回收 Entry 對象

if (!cleanSomeSlots(i, sz) && sz >= threshold)
	rehash();

cleanSomeSlots 就是進(jìn)行回收內(nèi)存:

ThreadLocal 源碼總結(jié)

通過源代碼可以看到每個線程都可以獨立修改屬于自己的副本而不會互相影響,從而隔離了線程和線程。避免了線程訪問實例變量發(fā)生安全問題。同時我們也能得出下面的結(jié)論:
(1)ThreadLocal 只是操作 Thread 中的 ThreadLocalMap 對象的集合;
(2)ThreadLocalMap 變量屬于線程的內(nèi)部屬性,不同的線程擁有完全不同的 ThreadLocalMap 變量;
(3)線程中的ThreadLocalMap變量的值是在ThreadLocal對象進(jìn)行set或者get操作時創(chuàng)建的;
(4)使用當(dāng)前線程的ThreadLocalMap的關(guān)鍵在于使用當(dāng)前的ThreadLocal的實例作為key來存儲value值;
(5) ThreadLocal模式至少從兩個方面完成了數(shù)據(jù)訪問隔離,即縱向隔離(線程與線程之間的 ThreadLocalMap不同)和橫向隔離(不同的ThreadLocal實例之間的互相隔離);
(6)一個線程中的所有的局部變量其實存儲在該線程自己的同一個map屬性中;
(7)線程死亡時,線程局部變量會自動回收內(nèi)存;
(8)線程局部變量時通過一個 Entry 保存在map中,該Entry 的key是一個 WeakReference包裝的ThreadLocal, value為線程局部變量,key 到 value 的映射是通過:ThreadLocal.threadLocalHashCode & (INITIAL_CAPACITY - 1) 來完成的;
(9)當(dāng)線程擁有的局部變量超過了容量的2/3(沒有擴大容量時是10個),會涉及到ThreadLocalMap中Entry的回收

對于多線程資源共享的問題,同步機制采用了“以時間換空間”的方式,而ThreadLocal采用了“以空間換時間”的方式。前者僅提供一份變量,讓不同的線程排隊訪問,而后者為每一個線程都提供了一份變量,因此可以同時訪問而互不影響。

synchronized

面試題

補充:volatile、ThreadLocal、synchronized等3個關(guān)鍵字區(qū)別

見轉(zhuǎn)載

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧


本文標(biāo)題:Java并發(fā)編程-創(chuàng)新互聯(lián)
本文地址:http://weahome.cn/article/cdoiei.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部