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