溫馨提示×

溫馨提示×

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

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

怎么實(shí)現(xiàn)JAVA 多線程的淺析

發(fā)布時間:2021-12-20 15:22:46 來源:億速云 閱讀:87 作者:柒染 欄目:編程語言

這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)怎么實(shí)現(xiàn)JAVA 多線程的淺析,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

JAVA 的多線程淺析

一、JAVA 語言的來源、及特點(diǎn)
在這個高速信息的時代,商家們紛紛把信息、產(chǎn)品做到Internet國際互連網(wǎng)頁上。再這些不尋常網(wǎng)頁的背后,要屬功能齊全、安全可靠的編程語言,Java是當(dāng)之無愧的。Java是由Sun Microsystem開發(fā)的一種功能強(qiáng)大的新型程序設(shè)計(jì)語言。是與平臺無關(guān)的編程語言。它是一種簡單的、面象對象的、分布式的、解釋的、鍵壯的、安全的、結(jié)構(gòu)的中立的、可移植的、性能很優(yōu)異的、多線程的、動態(tài)的、語言。
Java自問世以后,以其編程簡單、代碼高效、可移植性強(qiáng),很快受到了廣大計(jì)算機(jī)編程人士的青睞。Java語言是Internet上具有革命性的編程語言,它具有強(qiáng)大的動畫、多媒體和交互功能,他使World Web進(jìn)入了一個全新的時代。Java語言與C++極為類似,可用它來創(chuàng)建安全的、可移植的、多線程的交互式程序。另外用Java開發(fā)出來的程序與平臺無關(guān),可在多種平臺上運(yùn)行。后臺開發(fā),是一種高效、實(shí)用的編程方法。人們在屏幕前只能看到例如圖案、計(jì)算的結(jié)果等。實(shí)際上操作系統(tǒng)往往在后臺來調(diào)度一些事件、管理程序的流向等。例如操作系統(tǒng)中的堆棧,線程間的資源分配與管理,內(nèi)存的創(chuàng)建、訪問、管理等。可謂舉不盛舉。
· 二、JAVA的多線程理論
2.1引入
Java提供的多線程功能使得在一個程序里可同時執(zhí)行多個小任務(wù)。線程有時也稱小進(jìn)程是一個大進(jìn)程里分出來的小的獨(dú)立的進(jìn)程。因?yàn)镴ava實(shí)現(xiàn)的多線程技術(shù),所以比C和C++更鍵壯。多線程帶來的更大的好處是更好的交互性能和實(shí)時控制性能。當(dāng)然實(shí)時控制性能還取決于系統(tǒng)本身(UNIX, Windows,Macintosh等),在開發(fā)難易程度和性能上都比單線程要好。傳統(tǒng)編程環(huán)境通常是單線程的,由于JAVA是多線程的。盡管多線程是強(qiáng)大而靈巧的編程工具,但要用好卻不容易,且有許多陷阱,即使編程老手也難免誤用。為了更好的了解線程,用辦公室工作人員作比喻。辦公室工作人員就象 CPU,根據(jù)上級指示做工作,就象執(zhí)行一個線程。在單線程環(huán)境中,每個程序編寫和執(zhí)行的方式是任何時候程序只考慮一個處理順序。用我們的比喻,就象辦公室工作人員從頭到尾不受打擾和分心,只安排做一個工作。當(dāng)然,實(shí)際生活中工作人員很難一次只有一個任務(wù),更常見的是工作人員要同時做幾件事。老板將工作交給工作人員,希望工作人員做一這個工作,再做點(diǎn)那個工作,等等。如果一個任務(wù)無法做下去了,比如工作人員等待另一部門的信息,則工作人員將這個工作放在一邊,轉(zhuǎn)入另一個工作。一般來說,老板希望工作人員手頭的各個任務(wù)每一天都有一些進(jìn)展。這樣就引入了多線程的概念。多線程編程環(huán)境與這個典型的辦公室非常相似,同時給CPU分配了幾個任務(wù)或線程。和辦公室人員一樣,計(jì)算機(jī)CPU實(shí)際上不可能同一時間做幾件事,而是把時間分配到不同的線程,使每個線程都有點(diǎn)進(jìn)展。如果一個線程無法進(jìn)行,比如線程要求的鍵盤輸入尚未取得,則轉(zhuǎn)入另一線程的工作。通常,CPU在線程間的切換非常迅速,使人們感覺到好象所有線程是同時進(jìn)行的。
任何處理環(huán)境,無論是單線程還是多線程,都有三個關(guān)鍵方面。第一個是CPU,它實(shí)際上進(jìn)行計(jì)算機(jī)活動;第二個是執(zhí)行的程序的代碼;第三個是程序操作的數(shù)據(jù).。
在多線程編程中,每個線程都用編碼提供線程的行為,用數(shù)據(jù)供給編碼操作。多個線程可以同時處理同一編碼和數(shù)據(jù),不同的線程也可能各有不同的編碼和數(shù)據(jù)。事實(shí)上編碼和數(shù)據(jù)部分是相當(dāng)獨(dú)立的,需要時即可向線程提供。因此經(jīng)常是幾個線程使用同一編碼和不同的數(shù)據(jù)。這個思想也可以用辦公室工作人員來比喻。會計(jì)可能要做一個部門的帳或幾個或幾個部門的帳。任何情況的做帳的任務(wù)是相同的程序代碼,但每個部門的數(shù)據(jù)是不同的。會計(jì)可能要做整個公司的帳,這時有幾個任務(wù),但有些數(shù)據(jù)是共享的,因?yàn)楣編ば枰獊碜愿鱾€部門的數(shù)據(jù)。
多線程編程環(huán)境用方便的模型隱藏CPU在任務(wù)切換間的事實(shí)。模型允許假裝成有多個可用的CPU。為了建立另一個任務(wù),編程人員要求另一個虛擬CPU,指示它開始用某個數(shù)據(jù)組執(zhí)行某個程序段。下面我們來建立線程。
建立線程
在JAVA中建立線程并不困難,所需要的三件事:執(zhí)行的代碼、代碼所操作的數(shù)據(jù)和執(zhí)行代碼的虛擬CPU。虛擬CPU包裝在Thread類的實(shí)例中。建立Thread對象時,必須提供執(zhí)行的代碼和代碼所處理的數(shù)據(jù)。JAVA的面向?qū)ο竽P鸵蟪绦虼a只能寫成類的成員方法。數(shù)據(jù)只能作為方法中的自動(或本地)變量或類的成員存在。這些規(guī)則要求為線程提供的代碼和數(shù)據(jù)應(yīng)以類的實(shí)例的形式出現(xiàn)。
Public class SimpleRunnable implemants Runable{
Private String message;
Public static void main(String args[]){
SimpleRunnable r1=new SimpleRunnable(“Hello”);
Thread t1=new Thread(r1);
t1.start();
}
public SimpleRunnable(String message){
this.message=message;
}
public void run(){
for(;;){
System.out.println(message);
}
}
}
線程開始執(zhí)行時,它在public void run()方法中執(zhí)行。這種方法是定義的線程執(zhí)行的起點(diǎn),就象應(yīng)用程序從main()開始、小程序從init()開始一樣。線程操作的本地?cái)?shù)據(jù)是傳入線程的對象的成員。
首先,main()方法構(gòu)造SimpleRunnable類的實(shí)例。注意,實(shí)例有自己的數(shù)據(jù),這里是一個String,初始化為”Hello”.由于實(shí)例r1傳入Thread類構(gòu)造器,這是線程運(yùn)行時處理的數(shù)據(jù)。執(zhí)行的代碼是實(shí)例方法run()。
2.2 線程的管理
單線程的程序都有一個main執(zhí)行體,它運(yùn)行一些代碼,當(dāng)程序結(jié)束執(zhí)行后,它正好退出,程序同時結(jié)束運(yùn)行。在JAVA中我們要得到相同的應(yīng)答,必須稍微進(jìn)行改動。只有當(dāng)所有的線程退出后,程序才能結(jié)束。只要有一個線程一直在運(yùn)行,程序就無法退出。線程包括四個狀態(tài):new(開始),running (運(yùn)行),wait(等候)和done(結(jié)束)。第一次創(chuàng)建線程時,都位于new狀態(tài),在這個狀態(tài)下,不能運(yùn)行線程,只能等待。然后,線程或者由方法 start開始或者送往done狀態(tài),位于done中的線程已經(jīng)結(jié)束執(zhí)行,這是線程的最后一個狀態(tài)。一旦線程位于這個狀態(tài),就不能再次出現(xiàn),而且當(dāng) JAVA虛擬機(jī)中的所有線程都位于done狀態(tài)時,程序就強(qiáng)行中止。當(dāng)前正在執(zhí)行的所有線程都位于running狀態(tài),在程序之間用某種方法把處理器的執(zhí)行時間分成時間片,位于running狀態(tài)的每個線程都是能運(yùn)行的,但在一個給定的時間內(nèi),每個系統(tǒng)處理器只能運(yùn)行一個線程。與位于running狀態(tài)的線程不同,由于某種原因,可以把已經(jīng)位于waiting狀態(tài)的線程從一組可執(zhí)行線程中刪除。如果線程的執(zhí)行被中斷,就回到waiting狀態(tài)。用多種方法能中斷一個線程。線程能被掛起,在系統(tǒng)資源上等候,或者被告知進(jìn)入休眠狀態(tài)。該狀態(tài)的線程可以返回到running狀態(tài),也能由方法stop送入done 狀態(tài),
方法
描述
有效狀態(tài)
目的狀態(tài)
Start()
開始執(zhí)行一個線程
New
Running
Stop()
結(jié)束執(zhí)行一個線程
New或running
Done
Sleep(long)
暫停一段時間,這個時間為給定的毫秒
Running
Wait
Sleep(long,int)
暫停片刻,可以精確到納秒
Running
Wait
Suspend()
掛起執(zhí)行
Running
Wait
Resume()
恢復(fù)執(zhí)行
Wait
Running
Yield()
明確放棄執(zhí)行
Running
Running
2.3線程的調(diào)度
線程運(yùn)行的順序以及從處理器中獲得的時間數(shù)量主要取決于開發(fā)者,處理器給每個線程分配一個時間片,而且線程的運(yùn)行不能影響整個系統(tǒng)。處理器線程的系統(tǒng)或者是搶占式的,或者是非搶占式的。搶占式系統(tǒng)在任何給定的時間內(nèi)將運(yùn)行最高優(yōu)先級的線程,系統(tǒng)中的所有線程都有自己的優(yōu)先級。 Thread.NORM_PRIORITY是線程的缺省值,Thread類提供了setPriority和getPriority方法來設(shè)置和讀取優(yōu)先權(quán),使用setPriority方法能改變Java虛擬機(jī)中的線程的重要性,它調(diào)用一個整數(shù),類變量Thread.MIN_PRIORITY和 Thread.MAX_PRIORITY決定這個整數(shù)的有效范圍。Java虛擬機(jī)是搶占式的,它能保證運(yùn)行優(yōu)先級最高的線程。在JAVA虛擬機(jī)中我們把一個線程的優(yōu)先級改為最高,那么他將取代當(dāng)前正在運(yùn)行的線程,除非這個線程結(jié)束運(yùn)行或者被一條休眠命令放入waiting狀態(tài),否者將一直占用所有的處理器的時間。如果遇到兩個優(yōu)先級相同的線程,操作系統(tǒng)可能影響線程的執(zhí)行順序。而且這個區(qū)別取決于時間片(time slicing)的概念。
管理幾個線程并不是真正的難題,對于上百個線程它是怎樣管理的呢?當(dāng)然可以通過循環(huán),來執(zhí)行每一個線程,但是這顯然是冗長、乏味。JAVA創(chuàng)建了線程組。線程組是線程的一個譜系組,每個組包含的線程數(shù)不受限制,能對每個線程命名并能在整個線程組中執(zhí)行(Suspend)和停止(Stop)這樣的操作。
2.4信號標(biāo)志:保護(hù)其它共享資源
這種類型的保護(hù)被稱為互斥鎖。某個時間只能有一個線程讀取或修改這個數(shù)據(jù)值。在對文件尤其是信息數(shù)據(jù)庫進(jìn)行處理時,讀取的數(shù)據(jù)總是多于寫數(shù)據(jù),根據(jù)這個情況,可以簡化程序。下面舉一例,假設(shè)有一個雇員信息的數(shù)據(jù)庫,其中包括雇員的地址和電話號碼等信息,有時要進(jìn)行修改,但要更多的還是讀數(shù)據(jù),因此要盡可能防止數(shù)據(jù)被破壞或任意刪改。我們引入前面互斥鎖的概念,允許一個讀取鎖(red lock)和寫入鎖(write lock),可根據(jù)需要確定有權(quán)讀取數(shù)據(jù)的人員,而且當(dāng)某人要寫數(shù)據(jù)時,必須有互斥鎖,這就是信號標(biāo)志的概念。信號標(biāo)志有兩種狀態(tài),首先是empty() 狀態(tài),表示沒有任何線程正在讀或?qū)?,可以接受讀和寫的請求,并且立即提供服務(wù);第二種狀態(tài)是reading()狀態(tài),表示有線程正在從數(shù)據(jù)庫中讀信息,并記錄進(jìn)行讀操作的線程數(shù),當(dāng)它為0時,返回empty狀態(tài),一個寫請求將導(dǎo)致這個線程進(jìn)入等待狀態(tài)。
只能從empty狀態(tài)進(jìn)入writing狀態(tài),一旦進(jìn)入writing狀態(tài)后,其它線程都不能寫操作,任何寫或讀請求都必須等到這個線程完成寫操作為止,而且waiting狀態(tài)中的進(jìn)程也必須一直等到寫操作結(jié)束。完成操作后,返回到empty狀態(tài),發(fā)送一個通知信號,等待的線程將得到服務(wù)。
下面實(shí)現(xiàn)了這個信號標(biāo)志
class Semaphore{
final static int EMPTY=0;
final static int READING=1;
final static int WRITING=2;
protected int state=EMPTY;
protected int readCnt=0;
public synchronized void readLock(){
if(state==EMPTY){
state=READING;
}
else if(state==READING){
}
else if(state==WRITING){
while(state==WRITING){
try {wait();}
catch(InterruptedException e){;}
}
state=READING;
}
readCnt++;
return;
}
public synchronized void writeLock(){
if(state==EMPTY){
state=WRITING;
}
else{
while(state!=EMPTY){
try {wait();}
catch(InterruptedException e) {;}
}
}
}
public synchronized void readUnlock(){
readCnt--;
if(readCnt==0){
state=EMPTY;
notify();
}
}
public synchronized void writeUnlock(){
state=EMPTY;
notify();
}
}
現(xiàn)在是測試信號標(biāo)志的程序:
class Process extends Thread{
String op;
Semaphore sem;
Process(String name,String op,Semaphore sem){
super(name);
this.op=op;
this.sem=sem;
start();
}
public void run(){
if(op
catch(InterruptedException e){;}
System.out.println("Unlocking readLock:"+getName());
sem.readUnlock();
}
else if(op
catch(InterruptedException e){;}
System.out.println("Unlocking writeLock:"+getName());
sem.writeUnlock();
}
}
}
public class testSem{
public static void main(String argv[]){
Semaphore lock = new Semaphore();
new Process("1","read",lock);
new Process("2","read",lock);
new Process("3","write",lock);
new Process("4","read",lock);
}
}
testSem 類從process類的四個實(shí)例開始,它是個線程,用來讀或?qū)懸粋€共享文件。Semaphore類保證訪問不會破壞文件,執(zhí)行程序,輸出結(jié)果如下:
Trying to get readLock:1
Read op:1
Trying to get readLock:2
Read op:2
Trying to get writeLock:3
Trying to get readLock:4
Read op:4
Unlocking readLock:1
Unlocking readLock:2
Unlocking readLock:4
Write op:3
Unlocking writeLock:3
從這可看到,
2.5死鎖以及怎樣避免死鎖:
為了防止數(shù)據(jù)項(xiàng)目的并發(fā)訪問,應(yīng)將數(shù)據(jù)項(xiàng)目標(biāo)為專用,只有通過類本身的實(shí)例方法的同步區(qū)訪問。為了進(jìn)入關(guān)鍵區(qū),線程必須取得對象的鎖。假設(shè)線程要獨(dú)占訪問兩個不同對象的數(shù)據(jù),則必須從每個對象各取一個不同的鎖?,F(xiàn)在假設(shè)另一個線程也要獨(dú)占訪問這兩個對象,則該進(jìn)程必須得到這兩把鎖之后才能進(jìn)入。由于需要兩把鎖,編程如果不小心就可能出現(xiàn)死鎖。假設(shè)第一個線程取得對象A的鎖,準(zhǔn)備取對象B的鎖,而第二個線程取得了對象B的鎖,準(zhǔn)備取對象A的鎖,兩個線程都不能進(jìn)入,因?yàn)閮烧叨疾荒茈x開進(jìn)入的同步塊,既兩者都不能放棄目前持有的鎖。避免死鎖要認(rèn)真設(shè)計(jì)。線程因?yàn)槟硞€先決條件而受阻時,如需要鎖標(biāo)記時,不能讓線程的停止本身禁止條件的變化。如果要取得多個資源,如兩個不同對象的鎖,必須定義取得資源的順序。如果對象A和B的鎖總是按字母順序取得,則不會出現(xiàn)前面說道的餓死條件。
· 三、Java多線程的優(yōu)缺點(diǎn)
由于JAVA的多線程功能齊全,各種情況面面具到,它帶來的好處也是顯然易見的。多線程帶來的更大的好處是更好的交互性能和實(shí)時控制性能。當(dāng)然實(shí)時控制性能還取決于系統(tǒng)本身(UNIX,Windows,Macintosh 等),在開發(fā)難易程度和性能上都比單線程要好。當(dāng)然一個好的程序設(shè)計(jì)語言肯定也難免有不足之處。由于多線程還沒有充分利用基本OS的這一功能。這點(diǎn)我在前面已經(jīng)提到,對于不同的系統(tǒng),上面的程序可能會出現(xiàn)截然不同的結(jié)果,這使編程者偶會感到迷惑不解。

上述就是小編為大家分享的怎么實(shí)現(xiàn)JAVA 多線程的淺析了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(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