Java增强之并发编程
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Java增強(qiáng)之并發(fā)編程
1 多線程
1.1 進(jìn)程及線程
程序啟動(dòng)的時(shí)候,電腦會(huì)把這個(gè)程序加載到內(nèi)存,在內(nèi)存中需要給當(dāng)前的程序分配一段的獨(dú)立運(yùn)行的空間,這個(gè)空間就專門負(fù)責(zé)這個(gè)程序的運(yùn)行。每個(gè)應(yīng)用程序運(yùn)行都需要在內(nèi)存中有自己獨(dú)立的運(yùn)行空間,互不影響。進(jìn)程就是應(yīng)用程序在內(nèi)存中的獨(dú)立空間,負(fù)責(zé)當(dāng)前應(yīng)用程序的運(yùn)行,負(fù)責(zé)調(diào)度當(dāng)前程序中的所有運(yùn)行細(xì)節(jié)。
線程:位于進(jìn)程中,負(fù)責(zé)當(dāng)前進(jìn)程中的某個(gè)具備獨(dú)立運(yùn)行資質(zhì)的空間,負(fù)責(zé)程序中具體的某個(gè)獨(dú)功能的運(yùn)行。一個(gè)進(jìn)程中至少有一個(gè)線程,可以并發(fā)運(yùn)行多個(gè)線程。多線程之間共享同一個(gè)堆空間,每個(gè)線程都有自己獨(dú)立的??臻g
1.2 多線程
在一個(gè)進(jìn)程中,同時(shí)開啟多個(gè)線程,同時(shí)去完成某些任務(wù)。
一個(gè)進(jìn)程可以并發(fā)運(yùn)行多個(gè)線程,提高運(yùn)行效率。
1.3 多線程運(yùn)行的原理
CPU在線程中做時(shí)間片的切換。程序的運(yùn)行時(shí)CPU負(fù)責(zé)的,其實(shí)CPU在運(yùn)行程序時(shí),某個(gè)時(shí)刻只能運(yùn)行一個(gè)程序,CPU在多個(gè)程序之間進(jìn)行高速的切換,因?yàn)榍袚Q的頻率和速度太快了,所以我們是看不出來的。從而得知多線程雖然可以提高程序的運(yùn)行效率,但是不能無限制的開線程。
1.4 實(shí)現(xiàn)線程的方式
(1)繼承Thread類
(2)實(shí)現(xiàn)Runnable接口
(3)Callable和FutureTask創(chuàng)建線程
(4)通過線程池創(chuàng)建線程
代碼演示見之前的文章:https://blog.csdn.net/weixin_43786255/article/details/92065717
1.5 線程狀態(tài)圖解
詳細(xì)講解見:https://blog.csdn.net/weixin_43786255/article/details/92065717
2 java同步關(guān)鍵字
在多線程編程中,為了達(dá)到線程安全的目的,往往通過加鎖的方式來實(shí)現(xiàn)。
2.1 synchronized
是JVM級(jí)別的鎖,在編譯過程中,在指令級(jí)別加入一些標(biāo)識(shí)來實(shí)現(xiàn)。無法中斷正在阻塞隊(duì)列或者等待隊(duì)列的線程。synchronize是java語言的內(nèi)置特性,鎖的釋放是由jvm決定的,人工無法干預(yù) 。
常與wait(),notify(),notifyAll()方法聯(lián)用,調(diào)用wait()方法線程讓出CPU,釋放鎖,進(jìn)入等待狀態(tài)waitting,進(jìn)入等待隊(duì)列。當(dāng)其他線程調(diào)用notify()(隨機(jī)喚醒等待隊(duì)列中的一個(gè)線程)或notifyAll()(喚醒等待隊(duì)列中的全部線程)時(shí)沒會(huì)將隊(duì)列中的線程對(duì)象,放入第二個(gè)阻塞隊(duì)列,狀態(tài)是blocked,等待鎖被釋放后,開始競(jìng)爭(zhēng)鎖。
synchronize提供了偏向鎖,輕量級(jí)鎖,重量級(jí)鎖。
鎖的釋放時(shí)機(jī)有兩種情況:
? ? ? ? ? ?①獲取鎖的線程操作完成,該線程會(huì)自動(dòng)釋放鎖 ;②)獲取鎖的線程出現(xiàn)異常,jvm會(huì)自動(dòng)釋放。
synchronized存在的問題 :
? ? ? ? ? ?①如果獲取鎖標(biāo)記的線程不主動(dòng)釋放鎖,則未獲取標(biāo)記的只能等待,而且人工無法干預(yù);②當(dāng)多個(gè)線程讀寫文件時(shí),讀讀操作互相不影響,但是synchronized仍然無法同時(shí)執(zhí)行。
2.2 Lock
Lock鎖是Java代碼級(jí)別的,用戶可以主動(dòng)添加鎖,但是必須手動(dòng)釋放鎖。因此一般來說,使用 Lock 必須在 try{}catch{}塊中進(jìn)行,并且將釋放鎖的操作放在 finally 塊中進(jìn)行,以保證鎖一定被被釋放,防止死鎖的發(fā)生。提供了公平鎖,輪詢鎖,定時(shí)鎖,可中斷鎖等,還增加了多路通知機(jī)制(Condition),可以用一個(gè)鎖來管理多個(gè)同步塊。
與synchronized的區(qū)別:
? ? ? ? ? ?①synchronized 是 Java 語言的關(guān)鍵字,因此是內(nèi)置特性。Lock是一個(gè)類,通過這個(gè)類可以實(shí)現(xiàn)同步訪問;
? ? ? ? ? ?②synchronized 由系統(tǒng)自動(dòng)釋放,lock必須手動(dòng)釋放,否則可能產(chǎn)生死鎖。
常見方法如下:
| void lock() | 獲取鎖(阻塞,如果其他線程獲取到鎖,需要等待) |
| boolean tryLock() | 獲取鎖(非阻塞,如果其他線程獲取到鎖,則返回false) |
| boolean tryLock(long time, TimeUnit unit) | 獲取鎖(在指定的時(shí)間范圍內(nèi)獲取,超時(shí)返回false) |
| void lockInterruptibly() | 可中斷鎖 |
| void unlock() | 釋放鎖 |
ReentrantLock 類是唯一實(shí)現(xiàn)了Lock 接口的類,并且 ReentrantLock 提供了更多的方法。
區(qū)別對(duì)待讀、寫的操作用ReadWriteLock接口的實(shí)現(xiàn)類ReentrantReadWriteLock 里面提供了很多豐富的方法,不過最主要的有兩個(gè)方法:readLock()和 writeLock()用來獲取讀鎖和寫鎖。
ReadWriteLock將讀寫操作分離處理:
? ? ? ? ? ?①一個(gè)線程獲取讀鎖,另外的線程可以獲取讀鎖。但是不能獲取寫鎖(必須等待讀鎖釋放)。
? ? ? ? ? ?? ? ? ? ? ?②一個(gè)線程獲取寫鎖,那么一定要等待該線程釋放寫鎖,其他線程才能執(zhí)行讀寫操作。
2.3 死鎖
死鎖:兩個(gè)或兩個(gè)以上的線程在執(zhí)行過程中,因爭(zhēng)奪資源而造成的一種互相等待的現(xiàn)象,會(huì)讓程序掛起無法完成任務(wù)。
產(chǎn)生死鎖的條件:
? ? ? ? ? ①互斥條件:一個(gè)資源每次只能被一個(gè)進(jìn)程使用;
? ? ? ? ? ②請(qǐng)求與保持條件:一個(gè)進(jìn)程因請(qǐng)求資源而阻塞式,對(duì)已獲得的資源保持不放;
? ? ? ? ? ③不剝奪條件:進(jìn)程已獲得的資源,在未使用完成之前,不能強(qiáng)制剝奪;
? ? ? ? ? ④循環(huán)等待條件:若干進(jìn)程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系。
如何避免死鎖:避免相互等待,設(shè)置標(biāo)記位
死鎖處理:破壞條件
2.4 Volatile特殊域變量
首先了解在多線程編程中,要解決的問題主要有以下三方面:
? ? ? ? ? ? ①原子性:作為一個(gè)整體運(yùn)行
? ? ? ? ? ? ②可見性:多個(gè)線程修改的內(nèi)容是可見的。CPU不是直接和系統(tǒng)內(nèi)存通信,而是把變量讀到內(nèi)部的緩沖,也叫私? ? ? ? ? ? ? ?有的數(shù)據(jù)工作棧,修改也是在內(nèi)部緩存中,但是何時(shí)修改到系統(tǒng)內(nèi)存不能確定,這個(gè)時(shí)間差就可能導(dǎo)致讀到的值不是最新? ? ? ? ? ? ?值。
? ? ? ? ? ? ③指令重排:虛擬機(jī)把代碼編譯成指令后,出于優(yōu)化,保證代碼不變的情況下,會(huì)調(diào)整指令的執(zhí)行順序
valotile能夠滿足可見性和有序性,但無法保證原子性。
? ? ? ? ? ? ①保證可見性:在修改后強(qiáng)行把對(duì)變量的修改同步到系統(tǒng)內(nèi)存中,當(dāng)其他CPu在讀取自己內(nèi)部緩存中的值發(fā)現(xiàn)是? ? ? ? ? ? ? ? volatile修飾的時(shí)候會(huì)把內(nèi)部緩存中的值置為無效,從系統(tǒng)內(nèi)存中讀取。
? ? ? ? ? ? ②保證有序性:在某些指令中插入屏障指令,用于確保在向屏障指令后面繼續(xù)執(zhí)行的時(shí)候,前面的所有指令已經(jīng)? ? ? ? ? ? ? ? 輸入完畢。
原子性通過原子量:Atomicxxx:原子量保證數(shù)據(jù)的原子性
?
2.5 ThreadLocal線程變量副本
ThreadLocal提供了線程本地變量,訪問本地變量的每個(gè)線程都會(huì)拷貝一個(gè)變量到自己的本地內(nèi)存,多個(gè)線程操作這個(gè)變量的時(shí)候,實(shí)際上是操作自己本地內(nèi)存里面的變量,這樣就不會(huì)對(duì)其他線程產(chǎn)生影響。
ThreadLocal與同步機(jī)制都是為了解決多線程中相同變量的訪問沖突問題,ThreadLocal采用以時(shí)間換空間的方式,同步機(jī)制采用以空間換時(shí)間的方式
作用:保證線程的獨(dú)立變量。
應(yīng)用場(chǎng)景:有變量或?qū)ο髮?shí)例需要在線程中多個(gè)地方被重復(fù)使用,不希望線程之間共享,又不希望每次使用是都重新創(chuàng)建,加大內(nèi)存開銷。
與Synchronized的區(qū)別:Synchronized用于線程間的數(shù)據(jù)共享,而ThreadLocal則用于線程間的數(shù)據(jù)隔離。
使用:可以將可以將 ThreadLoad<T>視為 Map<Thread ,T>,把需要隔離的數(shù)據(jù)放入ThreadLocal,通過threadLocal.set(val)賦值,threadLocal.get()獲取值,最好不要放在線程池中,避免復(fù)用。
?
3 并發(fā)包
3.1 java并發(fā)包
jdk1.5版本以后,大多數(shù)的特性在 java.util.concurrent 包中,是專門用于多線程發(fā)編程的,充分利用了現(xiàn)代多處理器和多核心系統(tǒng)的功能以編寫大規(guī)模并發(fā)應(yīng)用程序。主要包含原子量、并發(fā)集合、同步器、可重入鎖,并對(duì)線程池的構(gòu)造提供了強(qiáng)力
的支持。
3.2 線程池
通過重用現(xiàn)有的線程池,而不是創(chuàng)建新的線程,可以在處理多個(gè)請(qǐng)求的時(shí)候分?jǐn)傇诰€程創(chuàng)建和銷毀過程中產(chǎn)生的巨大開銷,當(dāng)請(qǐng)求到達(dá)的時(shí)候工作線程已經(jīng)存在,不會(huì)由于等待創(chuàng)建線程而延遲執(zhí)行,從而提高系統(tǒng)的響應(yīng)性。
| ?public ThreadPoolExecutor(int corePoolSize, corePoolSize: 核心池的大小(常駐線程數(shù)) |
ThreadPoolExecutor的執(zhí)行順序:
? ? ? ? ? ?①當(dāng)線程數(shù)小于核心線程數(shù)時(shí),創(chuàng)建線程
? ? ? ? ? ?②當(dāng)線程數(shù)大于核心線程數(shù),且隊(duì)列未滿時(shí),將任務(wù)放入任務(wù)隊(duì)列
? ? ? ? ? ?③當(dāng)線程數(shù)大于核心線程數(shù)時(shí),且隊(duì)列任務(wù)已滿,若線程數(shù)小于最大線程數(shù)(核心線程數(shù)+有界隊(duì)列的數(shù)目),則創(chuàng)建線程;若線程數(shù)大于最大線程數(shù),則拋出異常,拒絕任務(wù)。
更多詳細(xì)講解見:https://blog.csdn.net/weixin_43786255/article/details/92065717
?
總結(jié)
以上是生活随笔為你收集整理的Java增强之并发编程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql tableveiw与表格,j
- 下一篇: Java集合Set,List和Map等