通過(guò)優(yōu)銳課核心java學(xué)習(xí)筆記中,我們可以看到,碼了很多專業(yè)的相關(guān)知識(shí), 分享給大家參考學(xué)習(xí)。我們將分兩部分介紹Java中的線程同步,以更好地理解Java的內(nèi)存模型。
專注于為中小企業(yè)提供成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)漢臺(tái)免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了成百上千企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過(guò)網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
介紹
Java線程同步和并發(fā)是復(fù)雜應(yīng)用程序各個(gè)設(shè)計(jì)階段中討論最多的主題。 線程,同步技術(shù)有很多方面,它們可以在應(yīng)用程序中實(shí)現(xiàn)高并發(fā)性。 多年來(lái),CPU(多核處理器,寄存器,高速緩存存儲(chǔ)器和主內(nèi)存(RAM))的發(fā)展已導(dǎo)致通常是開發(fā)人員往往忽略的某些領(lǐng)域-例如線程上下文,上下文切換,變量可見性,JVM內(nèi)存 型號(hào)與CPU內(nèi)存型號(hào)。
在本系列中,我們將討論Java內(nèi)存模型的各個(gè)方面,包括它如何影響線程上下文,Java中實(shí)現(xiàn)并發(fā)的同步技術(shù),競(jìng)爭(zhēng)條件等。在本文中,我們將重點(diǎn)討論線程,同步的概念 技術(shù)以及Java和我們的CPU的內(nèi)存模型。
概括
在深入研究線程和同步這一主題之前,讓我們快速回顧一下一些與線程相關(guān)的術(shù)語(yǔ)和概念。
1.Lock —鎖是線程同步機(jī)制。
2.
3.Java中的每個(gè)對(duì)象都有同步方法,wait()和notify()[也notifyAll()]。任何調(diào)用這些方法的線程都使用其監(jiān)視器獲得該對(duì)象的鎖。必須使用synced關(guān)鍵字來(lái)調(diào)用此方法,否則將拋出IllegealMonitorStateException。
4.信號(hào)是一種通知線程應(yīng)該繼續(xù)執(zhí)行的方法。這是使用對(duì)象方法wait(),notify()和notifyAll()實(shí)現(xiàn)的。調(diào)用方法notify()或notifyAll()可以使線程單一以喚醒后臺(tái)線程(通過(guò)調(diào)用方法wait())。
5.丟失信號(hào)-方法notify()和notifyAll()不保存方法調(diào)用,也不知道其他線程是否調(diào)用過(guò)wait()。如果一個(gè)線程在被通知的線程調(diào)用了wait()之前調(diào)用了notify(),則該信號(hào)將被等待的線程錯(cuò)過(guò)。這可能導(dǎo)致線程無(wú)休止地等待,因?yàn)樗e(cuò)過(guò)了信號(hào)
6.Runnable是一個(gè)功能性接口,可以由應(yīng)用程序中的任何類實(shí)現(xiàn),以便線程可以執(zhí)行它。
7.volatile是分配給變量以使類成為線程安全的另一個(gè)關(guān)鍵字。要了解此關(guān)鍵字的用法,必須了解CPU體系結(jié)構(gòu)和JVM內(nèi)存模型。我們稍后再討論。
8.ThreadLocal允許創(chuàng)建只能由所有者線程讀取/寫入的變量。這用于使代碼安全。
9.Thread Pool是線程的集合,線程將在其中執(zhí)行任務(wù)。線程的創(chuàng)建和維護(hù)非常受服務(wù)控制。在Java中,線程池由ExecutorService的實(shí)例表示。
10.ThreadGroup是該類提供一種用于將多個(gè)線程收集到單個(gè)對(duì)象中的機(jī)制,并允許我們一次操縱/控制這些線程。
11.Daemon線程-這些線程在后臺(tái)運(yùn)行。守護(hù)程序線程的一個(gè)很好的例子是Java Garbage Collector。 JVM在退出以完成其執(zhí)行之前不等待守護(hù)程序線程(而JVM在等待非守護(hù)程序線程或用戶線程完成其執(zhí)行之前)。
12.synchronized-關(guān)鍵字,用于在多個(gè)線程必須在并發(fā)模式下執(zhí)行同一功能時(shí)控制單個(gè)線程執(zhí)行代碼。此關(guān)鍵字可用于方法和代碼塊以實(shí)現(xiàn)線程安全。請(qǐng)注意,此關(guān)鍵字沒有超時(shí),因此有可能發(fā)生死鎖情況。
13.死鎖-一種情況,一個(gè)或多個(gè)線程正在等待對(duì)象鎖被另一線程釋放。導(dǎo)致死鎖的可能情況是線程正在相互等待釋放鎖的地方!
14.虛假喚醒-由于莫名其妙的原因,即使未調(diào)用notify()和notifyAll(),線程也有可能喚醒。這是一個(gè)虛假的喚醒。為了解決此問題,喚醒的線程圍繞自旋鎖中的條件自旋。
Java
1
public synchronized doWait() {
2
?while(!wasSignalled) { // spin-lock check to avoid spurious wake up calls
3
? ?wait();
4
}
5
?// do something
6
}
7
8
public synchronized doNotify() {
9
?wasSignalled = true;
10
?notify();
11
}
線程匱乏
當(dāng)沒有為某個(gè)線程分配CPU時(shí)間(因?yàn)槠渌€程占用了所有線程)時(shí),就會(huì)發(fā)生線程饑餓。 (例如,在某個(gè)對(duì)象上等待的線程(調(diào)用了wait())保持無(wú)限期等待,因?yàn)槠渌€程不斷被喚醒(通過(guò)調(diào)用notify())。
為了減輕這種情況,我們可以使用Thread.setPriority(int priority)方法為線程設(shè)置優(yōu)先級(jí)。 優(yōu)先級(jí)參數(shù)必須在Thread.MIN_PRIORITY到Thread.MAX_PRIORITY之間的設(shè)置范圍內(nèi)。 查看官方線程文檔以獲取有關(guān)線程優(yōu)先級(jí)的更多信息。
鎖定界面與同步關(guān)鍵字
1.無(wú)法在同步塊或方法中具有超時(shí)。 這可能會(huì)在應(yīng)用程序似乎掛起,死鎖等情況下結(jié)束。同步塊必須僅包含在單個(gè)方法中。
Java
1
class CustomLock {
2
3
?private boolean isLocked = false;
4
5
?public synchronized void lock()
6
? throws InterruptedException {
7
? ?
8
? ?isLocked = true;
9
? ?while(isLocked) {
10
? ? ?// calling thread releases the lock it holds on the monitor
11
? ? ?// object. Multiple threads can call wait() as the monitor is released.
12
? ? ?wait();
13
? }
14
}
15
16
?public synchronized void unlock() {
17
? ?isLocked = false;
18
? ?notify();
19
? ?// only after the lock is released in this block, the wait() block
20
? ?// above can re-acquire the lock on this object's monitor.
21
}
22
}
線程執(zhí)行
我們可以通過(guò)兩種方式在Java中執(zhí)行線程。 他們是:
1.擴(kuò)展Thread類并調(diào)用start()方法。 (這不是從Thread子類化類的首選方式,因?yàn)樗鼫p少了添加該類更多功能的范圍。)
2.實(shí)現(xiàn)Runnable或Callable接口 這兩個(gè)接口都是功能性接口,這意味著它們都只定義了一個(gè)抽象方法。 (將來(lái)也可以通過(guò)實(shí)現(xiàn)其他接口來(lái)擴(kuò)展作為類的首選方法。)
可運(yùn)行的界面
這是用于通過(guò)線程執(zhí)行特定任務(wù)的基本接口。 此接口僅描述一種方法,稱為帶有無(wú)效返回類型的run()。 如果必須在線程中執(zhí)行任何功能,但不期望返回類型,請(qǐng)實(shí)現(xiàn)此接口。 基本上,在失敗的情況下,無(wú)法檢索線程的結(jié)果或任何異?;蝈e(cuò)誤。
通話界面
這是一個(gè)接口,除了獲得執(zhí)行結(jié)果之外,還用于通過(guò)線程執(zhí)行特定任務(wù)。 該接口遵循泛型。 對(duì)于實(shí)現(xiàn)此接口的類,它僅描述了一種稱為call()的方法,并描述了返回類型。 如果必須在線程中執(zhí)行任何功能并且必須捕獲執(zhí)行結(jié)果,請(qǐng)實(shí)現(xiàn)此接口。
同步技術(shù)
如上所述,可以使用同步關(guān)鍵字或使用Lock實(shí)例來(lái)同步線程。 Lock接口的基本實(shí)現(xiàn)是ReentrantLock類。 同樣,用于讀/寫操作的Lock接口也有所不同。
當(dāng)線程試圖讀取或?qū)懭胭Y源時(shí),這有助于應(yīng)用程序?qū)崿F(xiàn)更高的并發(fā)性。 此實(shí)現(xiàn)稱為ReentrantReadWriteLock。 這兩個(gè)類之間的主要區(qū)別如下所示:
Class ReentrantLock ReentrantReadWriteLock類
只允許訪問1個(gè)線程以進(jìn)行讀取或?qū)懭?,但不能同時(shí)訪問兩者。 如果操作正在讀取資源,則一次允許訪問多個(gè)/所有線程。
如果該操作是寫操作,則一次只能訪問一個(gè)線程。
鎖定讀寫操作的資源,使操作互斥。 鎖定讀寫操作的資源,使操作互斥。
由于資源被鎖定(甚至用于讀取操作),因此降低了性能。 在性能方面更好,因?yàn)樗鼮橐獔?zhí)行讀取操作的所有線程提供并發(fā)訪問權(quán)限。
請(qǐng)參閱下面的ReentrantReadWriteLock示例,以了解如何在僅允許一個(gè)線程更新資源的同時(shí)實(shí)現(xiàn)對(duì)資源的并發(fā)讀取。
注意:資源可以是應(yīng)用程序中各種線程嘗試同時(shí)訪問的任何數(shù)據(jù)。
Java
1
public class ConcurrentReadWriteResourceExample {
2
3
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
4
private ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
5
private ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
6
7
private void readResource() {
8
readLock.lock();
9
// read the resource from a file, cache, database or from memory
10
? ? ? ?// this block can be accessed by 'N' threads concurrently for reading
11
readLock.unlock();
12
}
13
14
private void writeResource(String value) {
15
writeLock.lock();
16
// write or update value to either a file, cache, database or from memory
17
? ? ? ?// this block can be accessed by at-most '1' thread at a time for writing
18
writeLock.unlock();
19
}
20
}
創(chuàng)建上述類的一個(gè)實(shí)例,并將其傳遞給多個(gè)線程; 將處理以下內(nèi)容:
??'N'個(gè)線程使用readLock或最多一個(gè)線程使用writeLock。
??讀或?qū)懚疾粫?huì)同時(shí)發(fā)生。
Java內(nèi)存模型和CPU
有關(guān)Java和CPU內(nèi)存模型的說(shuō)明將幫助我們更好地了解對(duì)象和變量在Java堆/線程堆棧中的存儲(chǔ)方式以及實(shí)際CPU內(nèi)存的存儲(chǔ)方式。 現(xiàn)代的CPU由寄存器組成,這些寄存器充當(dāng)處理器本身的直接存儲(chǔ)器,高速緩存存儲(chǔ)器-每個(gè)處理器都有一個(gè)高速緩存層來(lái)存儲(chǔ)數(shù)據(jù),最后是存在應(yīng)用程序數(shù)據(jù)的RAM或主存儲(chǔ)器。
JVM與CPU內(nèi)存模型
在硬件或CPU上,線程堆棧和堆都位于主內(nèi)存中。 線程堆棧和堆的某些部分有時(shí)可能會(huì)出現(xiàn)在CPU緩存和內(nèi)部寄存器中。 以下是由于上述體系結(jié)構(gòu)而可能發(fā)生的問題:
1,并非所有訪問變量的線程都立即看到對(duì)共享變量的線程更新(寫)的可見性。
2.讀取,檢查和更新共享變量的數(shù)據(jù)時(shí)的種族條件。
volatile關(guān)鍵字
volatile關(guān)鍵字是Java 5中引入的,在實(shí)現(xiàn)線程安全方面有重要的用途。 此關(guān)鍵字可用于基元和對(duì)象。 在變量上使用volatile關(guān)鍵字可確保在更新后直接從主存儲(chǔ)器讀取給定變量并將其寫回主存儲(chǔ)器。
ThreadLocal類別
線程同步的最后一個(gè)主題是Lock是Java類ThreadLocal之后的主題。 此類可創(chuàng)建只能由同一線程讀取/寫入的變量。 這為我們提供了一種通過(guò)定義線程局部變量來(lái)實(shí)現(xiàn)線程安全的簡(jiǎn)單方法。 ThreadLocal在線程池或ExecutorService中有重要用途,因此每個(gè)線程都使用自己的某些資源或?qū)ο蟮膶?shí)例。
例如,對(duì)于每個(gè)線程,都需要一個(gè)單獨(dú)的數(shù)據(jù)庫(kù)連接,或者一個(gè)單獨(dú)的計(jì)數(shù)器。 在這種情況下,ThreadLocal會(huì)有所幫助。 這在Spring Boot應(yīng)用程序中也使用,其中為每個(gè)傳入呼叫設(shè)置了用戶上下文(Spring Security),并且將通過(guò)各種實(shí)例在線程流之間共享用戶上下文。 在以下情況下使用ThreadLocal
?線程限制。
?每個(gè)線程的數(shù)據(jù)性能。
?每個(gè)線程上下文。
Java
1
/**
2
* This is a demo class only. The ThreadLocal snippet can be applied
3
* to any number of threads and you can see that each thread gets it's
4
* own instance of the ThreadLocal. This achieves thread safety.
5
*/
6
public class ThreadLocalDemo {
7
8
public static void main(String...args) {
9
ThreadLocal<String> threadLocal = new ThreadLocal<String>() {
10
protected String initialValue() {
11
return "Hello World!";
12
}
13
};
14
15
// below line prints "Hello World!"
16
System.out.println(threadLocal.get());
17
18
// below line sets new data into ThreadLocal instance
19
threadLocal.set("Good bye!!!");
20
21
// below line prints "Good bye!!!"
22
System.out.println(threadLocal.get());
23
24
// below line removes the previously set message
25
threadLocal.remove();
26
27
// below line prints "Hello World!" as the initial value will be
28
// applied again
29
System.out.println(threadLocal.get());
30
}
31
}
線程同步和相關(guān)概念就是這樣。 并發(fā)將在本文的第2部分中介紹。
喜歡這篇文章的可以點(diǎn)個(gè)贊,歡迎大家留言評(píng)論,記得關(guān)注我,每天持續(xù)更新技術(shù)干貨、職場(chǎng)趣事、海量面試資料等等
?> 如果你對(duì)java技術(shù)很感興趣也可以交流學(xué)習(xí),共同學(xué)習(xí)進(jìn)步。?
不要再用"沒有時(shí)間“來(lái)掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來(lái)的自己一個(gè)交代
文章寫道這里,歡迎完善交流。最后奉上近期整理出來(lái)的一套完整的java架構(gòu)思維導(dǎo)圖,分享給大家對(duì)照知識(shí)點(diǎn)參考學(xué)習(xí)。有更多JVM、MySQL、Tomcat、Spring Boot、Spring Cloud、Zookeeper、Kafka、RabbitMQ、RockerMQ、redis、ELK、Git等Java干貨
本文名稱:Java線程同步和并發(fā)第1部分
標(biāo)題URL:http://jinyejixie.com/article14/gpgide.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站制作、建站公司、標(biāo)簽優(yōu)化、做網(wǎng)站、定制開發(fā)、網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)