溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么進(jìn)行Java多線程概念和使用原理的分析

發(fā)布時(shí)間:2021-11-10 10:02:26 來源:億速云 閱讀:147 作者:柒染 欄目:大數(shù)據(jù)

本篇文章給大家分享的是有關(guān)怎么進(jìn)行Java多線程概念和使用原理的分析,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

首選了解一下什么是進(jìn)程和線程的概念?

進(jìn)程是運(yùn)行中的應(yīng)用程序,每個(gè)進(jìn)程都有自己獨(dú)立的一塊內(nèi)存空間,一個(gè)進(jìn)程可以啟動(dòng)多個(gè)線程,而線程是指進(jìn)程中的一個(gè)執(zhí)行流程,也可以理解成一段代碼。比如java.exe進(jìn)程中可以運(yùn)行很多線程,線程永遠(yuǎn)屬于某個(gè)進(jìn)程,進(jìn)程中的多個(gè)線程共享進(jìn)程的內(nèi)存。

在Java中為了創(chuàng)建一個(gè)新的線程,必須指明線程所要執(zhí)行的代碼。通過Java提供的類java.lang.Thread來方便多線程編程,這個(gè)類提供了大量的方法來方便我們控制自己的各個(gè)線程。那么如何通過Java線程來執(zhí)行的代碼呢?

Thread類中提供了一個(gè)最重要的run()方法,它被Thread類的start()方法調(diào)用,提供給我們線程要執(zhí)行的代碼。為了執(zhí)行我們自己的代碼只需要重寫該方法即可。

方式一:繼承Thread類,重寫run()方法

創(chuàng)建Thread類的子類繼承該對象重寫run()方法,具體代碼實(shí)例如下:

怎么進(jìn)行Java多線程概念和使用原理的分析

上述方法簡單明了符合大家的寫作習(xí)慣,但是有一個(gè)很大的缺點(diǎn),那就是如果我們的類已經(jīng)是其他類的子類,就無法再繼承Thread類,此時(shí)如果不想創(chuàng)建一個(gè)新的類,應(yīng)該怎么辦呢?

我們只能將我們的方法作為參數(shù)傳遞給Thread類的實(shí)例,類似回調(diào)函數(shù),但是Java是沒有指針的,我們只能傳遞一個(gè)包含該方法的實(shí)例。Java使用接口java.lang.Runnable解決這個(gè)問題。

Runnable接口只有一個(gè)run()方法,聲明類實(shí)現(xiàn)Runnable接口并提供這一方法,將線程代碼寫入這個(gè)方法完成任務(wù),但是Runnable接口并沒有任何對線程的支持,因此必須創(chuàng)建Thread類的實(shí)例,通過Thread類的構(gòu)造函數(shù)完成。

方式二:實(shí)現(xiàn)Runnable接口,代碼實(shí)例如下:

怎么進(jìn)行Java多線程概念和使用原理的分析

使用Runnable接口來實(shí)現(xiàn)多線程使得我們能夠在一個(gè)類中包容所有的代碼,有利于封裝。缺點(diǎn)在于只能使用一套代碼,若想創(chuàng)建多個(gè)線程并使各個(gè)線程執(zhí)行不同的代碼,則仍必須額外創(chuàng)建類,如果這樣的話,在大多數(shù)情況下也許還不如直接用多個(gè)類分別繼承Thread來得方便。綜上所述比較兩種實(shí)現(xiàn)多線程的方法各有千秋,大家可以靈活運(yùn)用。

下面和大家分析一下多線程在使用中的一些常見問題。

一:線程可以劃分為七種狀態(tài)(有些人認(rèn)為只有五種,將鎖池和等待池看成阻塞狀態(tài)的特殊情況,這種認(rèn)知也是正確的但是鎖池和等待池單獨(dú)分離有利于對程序的理解)。

1.初始狀態(tài),線程創(chuàng)建,線程對象調(diào)用start()方法

2.可運(yùn)行狀態(tài),也就是等待CPU資源,等待運(yùn)行的狀態(tài)

3.運(yùn)行狀態(tài),獲得了CPU資源,正在運(yùn)行狀態(tài)

4.阻塞狀態(tài),也就是讓出CPU資源,進(jìn)入一種等待狀態(tài),而且不是可運(yùn)行狀態(tài),有三種情況會(huì)進(jìn)入阻塞狀態(tài)。

1)如等待輸入(輸入設(shè)備進(jìn)行處理,而CUP不處理),則放入阻塞,直到輸入完畢,阻塞結(jié)束后會(huì)進(jìn)入可運(yùn)行狀態(tài)。

2)線程休眠,線程對象調(diào)用sleep()方法,阻塞結(jié)束后會(huì)進(jìn)入可運(yùn)行狀態(tài)。

3)線程對象 2 調(diào)用線程對象 1 的join()方法,那么線程對象 2 進(jìn)入阻塞狀態(tài),直到線程對象 1 中止。

5.中止?fàn)顟B(tài),也就是執(zhí)行結(jié)束

6.鎖池狀態(tài)

7.等待隊(duì)列

二:線程優(yōu)先級

線程的優(yōu)先級代表該線程的重要程度,當(dāng)有多個(gè)線程同時(shí)處于可執(zhí)行狀態(tài)并等待獲得CPU時(shí)間時(shí),線程調(diào)度系統(tǒng)根據(jù)各個(gè)線程的優(yōu)先級來決定給誰分配CPU時(shí)間,優(yōu)先級高的線程有更大的機(jī)會(huì)獲得CPU時(shí)間,優(yōu)先級低的線程獲取CPU時(shí)間幾率比較小。調(diào)用Thread類的getPriority()和setPriority()方法來存取線程的優(yōu)先級,線程的優(yōu)先級界于1(MIN_PRIORITY)和10(MAX_PRIORITY)之間,缺省是5(NORM_PRIORITY)。

三:線程同步

由于同一進(jìn)程的多個(gè)線程共享同一片存儲(chǔ)空間,在帶來方便的同時(shí),也帶來了訪問沖突嚴(yán)重的問題。Java語言提供了專門的機(jī)制以解決這種沖突,有效避免了同一個(gè)數(shù)據(jù)對象被多個(gè)線程同時(shí)訪問。由于我們可以通過private關(guān)鍵字來保證數(shù)據(jù)對象只能被方法訪問,所以我們只需針對方法提出一套機(jī)制,這套機(jī)制就是synchronized關(guān)鍵字,它包括兩種用法:synchronized方法和synchronized塊。

1. 通過在方法聲明中加入synchronized關(guān)鍵字來聲明synchronized方法。如:public synchronized void accessVal(int newVal);synchronized方法控制對類成員變量的訪問:每個(gè)類實(shí)例對應(yīng)一把鎖,每個(gè)synchronized方法都必須獲得調(diào)用該方法的類實(shí)例的鎖方能執(zhí)行,否則所屬線程阻塞,方法一旦執(zhí)行,就獨(dú)占該鎖,直到從該方法返回時(shí)才將鎖釋放,此后被阻塞的線程方能獲得該鎖,重新進(jìn)入可執(zhí)行狀態(tài)。

這種機(jī)制確保了同一時(shí)刻對于每一個(gè)類實(shí)例,其所有聲明為synchronized的成員函數(shù)中至多只有一個(gè)處于可執(zhí)行狀態(tài),從而有效避免了類成員變量的訪問沖突。在Java中不單單只有類實(shí)例,每一個(gè)類也是對應(yīng)一把鎖,這樣我們也可將類的靜態(tài)成員函數(shù)聲明為synchronized,以控制其對類的靜態(tài)成員變量的訪問。

synchronized方法的缺陷:若將一個(gè)大的方法聲明為synchronized將會(huì)大大影響效率,典型地,若將線程類的run()方法聲明為synchronized,由于在線程的整個(gè)生命期內(nèi)它一直在運(yùn)行,因此將導(dǎo)致它對本類任何synchronized方法的調(diào)用都永遠(yuǎn)不會(huì)成功。當(dāng)然我們可以通過將訪問類成員變量的代碼放到專門的方法中將其聲明為synchronized,并在主方法中調(diào)用來解決這一問題,但是Java為我們提供了更好的解決辦法,那就是synchronized塊。

2. 通過synchronized關(guān)鍵字來聲明synchronized塊,語法如下:

synchronized(syncObject) {

 //允許訪問控制的代碼

}

synchronized塊是一個(gè)代碼塊,其中的代碼必須獲得對象syncObject(類實(shí)例或類)的鎖方能執(zhí)行。由于可以針對任意代碼塊且可任意指定上鎖的對象,因此靈活性比較高。

四:線程阻塞

為了解決對共享存儲(chǔ)區(qū)的訪問沖突,Java引入了同步機(jī)制。阻塞指的是暫停一個(gè)線程的執(zhí)行以等待某個(gè)條件發(fā)生(如某資源就緒),Java提供了大量方法來支持阻塞,下面逐一進(jìn)行分析:

1. sleep()方法:sleep()允許指定以毫秒為單位的一段時(shí)間作為參數(shù),它使得線程在指定的時(shí)間內(nèi)進(jìn)入阻塞狀態(tài),不能得到CPU時(shí)間,指定的時(shí)間一過,線程重新進(jìn)入可執(zhí)行狀態(tài)。

2. suspend()和resume()方法:兩個(gè)方法配合使用,suspend()使得線程進(jìn)入阻塞狀態(tài)不會(huì)自動(dòng)恢復(fù),必須其對應(yīng)的resume()被調(diào)用,才能使得線程重新進(jìn)入可執(zhí)行狀態(tài)。

3. yield()方法:yield()使得線程放棄當(dāng)前分得的CPU時(shí)間,但是不使線程阻塞,即線程仍處于可執(zhí)行狀態(tài),隨時(shí)可能再次分得CPU時(shí)間。調(diào)用yield()的效果等價(jià)于調(diào)度程序認(rèn)為該線程已執(zhí)行了足夠的時(shí)間從而轉(zhuǎn)到另一個(gè)線程。

4. wait()和 notify()方法:兩個(gè)方法配合使用,wait()使得線程進(jìn)入阻塞狀態(tài),它有兩種形式,一種允許指定以毫秒為單位的一段時(shí)間作為參數(shù),另一種沒有參數(shù),前者當(dāng)對應(yīng)的notify()被調(diào)用或者超出指定時(shí)間時(shí)線程重新進(jìn)入可執(zhí)行狀態(tài),后者則必須對應(yīng)的notify()被調(diào)用。注意的是1 2 3 步驟阻塞時(shí)都不會(huì)釋放占用的鎖而這一對方法則會(huì)釋放占用的鎖。

關(guān)于 wait()和 notify()方法最后再說明兩點(diǎn):

1)調(diào)用notify()方法導(dǎo)致解除阻塞的線程是因?yàn)檎{(diào)用該對象的wait()方法而阻塞的線程中隨機(jī)選取的,我們無法預(yù)料哪一個(gè)線程將會(huì)被選擇,所以編程時(shí)要特別小心,避免因這種不確定性而產(chǎn)生問題。

2)除了notify(),還有一個(gè)notifyAll()方法也可起到類似作用,唯一的區(qū)別在于,調(diào)用notifyAll()方法將把因?yàn)檎{(diào)用該對象的wait()方法而阻塞的所有線程一次性全部解除阻塞。當(dāng)然,只有獲得鎖的那一個(gè)線程才能進(jìn)入可執(zhí)行狀態(tài)。

說到阻塞就不能不說一說死鎖的概念,略一分析就能發(fā)現(xiàn)suspend()方法和不指定超時(shí)期限的wait()方法的調(diào)用都可能產(chǎn)生死鎖。遺憾的是Java并不在語言級別上支持死鎖的避免,因此我們在編程中必須小心地避免死鎖。

五:守護(hù)線程

守護(hù)線程是一類特殊的線程,它和普通線程的區(qū)別在于它并不是應(yīng)用程序的核心部分,當(dāng)一個(gè)應(yīng)用程序的所有非守護(hù)線程終止運(yùn)行時(shí),即使仍然有守護(hù)線程在運(yùn)行,應(yīng)用程序也將終止,相反只要有一個(gè)非守護(hù)線程在運(yùn)行應(yīng)用程序就不會(huì)終止。守護(hù)線程一般被用于在后臺為其它線程提供服務(wù)??梢酝ㄟ^調(diào)用isDaemon()方法來判斷一個(gè)線程是否是守護(hù)線程,可以調(diào)用setDaemon()方法來將一個(gè)線程設(shè)為守護(hù)線程。

六:線程組

線程組是Java特有的一個(gè)概念,在Java中線程組是ThreadGroup類的對象,每個(gè)線程都隸屬于唯一一個(gè)線程組,這個(gè)線程組在線程創(chuàng)建時(shí)指定并在線程的整個(gè)生命期內(nèi)都不能更改。你可以通過調(diào)用包含 ThreadGroup 類型參數(shù)的 Thread 類構(gòu)造函數(shù)來指定線程屬的線程組,若沒有指定則線程缺省的隸屬于名為system的系統(tǒng)線程組。

在Java中除了預(yù)建的系統(tǒng)線程組外,所有線程組都必須顯式創(chuàng)建并且每個(gè)線程組又隸屬于另一個(gè)線程組。你可以在創(chuàng)建線程組時(shí)指定其所隸屬的線程組,若沒有指定則缺省的隸屬于系統(tǒng)線程組。這樣所有線程組組成了一棵以系統(tǒng)線程組為根的樹。

Java線程組實(shí)例代碼,具體如下:

怎么進(jìn)行Java多線程概念和使用原理的分析

源碼參考:http://blog.yoodb.com/yoodb/article/detail/1330

Java允許我們對一個(gè)線程組中的所有線程同時(shí)進(jìn)行操作,比如我們可以通過調(diào)用線程組的相應(yīng)方法來設(shè)置其中所有線程的優(yōu)先級,也可以啟動(dòng)或阻塞其中的所有線程。

Java的線程組機(jī)制的另一個(gè)重要作用是線程安全。線程組機(jī)制允許我們通過分組來區(qū)分有不同安全特性的線程,對不同組的線程進(jìn)行不同的處理,還可以通過線程組的分層結(jié)構(gòu)來支持不對等安全措施的采用。Java 的 ThreadGroup 類提供了大量的方法來方便我們對線程組樹中的每一個(gè)線程組以及線程組中的每一個(gè)線程進(jìn)行操作。

以上就是怎么進(jìn)行Java多線程概念和使用原理的分析,小編相信有部分知識點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見到或用到的。希望你能通過這篇文章學(xué)到更多知識。更多詳情敬請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI