一般來(lái)說(shuō),我們把正在計(jì)算機(jī)中執(zhí)行的程序叫做"進(jìn)程"(Process) ,而不將其稱為程序(Program)。
所謂"線程"(Thread),是"進(jìn)程"中某個(gè)單一順序的控制流。新興的操作系統(tǒng),如Mac,Windows NT,Windows 95等,大多采用多線程的概念,把線 程視為基本執(zhí)行單位。
線程也是Java中的相當(dāng)重要的組成部分之一。 甚至最簡(jiǎn)單的Applet也是由多個(gè)線程來(lái)完成的。
在Java中, 任何一個(gè)Applet的paint()和update()方法都是由AWT(Abstract Window Toolkit)繪圖與事件處理線程調(diào)用的,而Applet 主要的里程碑方法——init(),start(),stop()和destory() ——是由執(zhí)行該Applet的應(yīng)用調(diào)用的。 單線程的概念沒(méi)有什么新的地方,真正有趣的是在一個(gè)程序中同時(shí)使用多個(gè)線程來(lái)完成不同的任務(wù)。
某些地方用輕量進(jìn)程(Lightweig ht Process)來(lái)代替線程,線程與真正進(jìn)程的相似性在于它們都是單一順序控制流。然而線程被認(rèn)為輕量是由于它運(yùn)行于整個(gè)程序的上下文內(nèi),能使用整個(gè)程序共有的資源和程序環(huán)境。
作為單一順序控制流,在運(yùn)行的程序內(nèi)線程必須擁有一些資源作為必要的開(kāi)銷。例如,必須有執(zhí)行堆棧和程序計(jì)數(shù)器在線程內(nèi)執(zhí)行的代碼只在它的上下文中起作用,因此某些地方用"執(zhí)行上下文"來(lái)代替"線程"。
線程屬性 為了正確有效地使用線程,必須理解線程的各個(gè)方面并了解Java 實(shí)時(shí)系統(tǒng)。 必須知道如何提供線程體、線程的生命周期、實(shí)時(shí)系統(tǒng)如何調(diào)度線程、線程組、什么是幽靈線程(Demo nThread)。
Java多線程程序需要我們具體的學(xué)習(xí)相關(guān)語(yǔ)法。
其中我們要了解相關(guān)的語(yǔ)法究竟有什么。很多重要的問(wèn)題成本都在細(xì)節(jié),基本的語(yǔ)法就是Java多線程程序的細(xì)節(jié)。
希望大家有所收獲。 Java多線程程序中經(jīng)常用到的方法有以下幾個(gè):run(),start(),wait(),sleep(),notify(),notifyAll(),yield(),join(),還有一個(gè)重要的關(guān)鍵字 synchronized。
下面分別對(duì)這些方法進(jìn)行解釋: run()和start() 這兩個(gè)方法應(yīng)該都比較熟悉,把需要并行處理的代碼放在run()方法中,start()方法啟動(dòng)線程將自動(dòng)調(diào)用 run()方法,這是由Java的內(nèi)存機(jī)制規(guī)定的。并且run()方法必須是public訪問(wèn)權(quán)限,返回值類型為void。
關(guān)鍵字Synchronized 這個(gè)關(guān)鍵字用于保護(hù)共享數(shù)據(jù),當(dāng)然前提是要分清哪些數(shù)據(jù)是共享數(shù)據(jù)。每個(gè)對(duì)象都有一個(gè)鎖標(biāo)志,當(dāng)一個(gè)線程訪問(wèn)該對(duì)象時(shí),被Synchronized修飾的數(shù)據(jù)將被“上鎖”,阻止其他線程訪問(wèn)。
當(dāng)前線程訪問(wèn)完這部分?jǐn)?shù)據(jù)后釋放鎖標(biāo)志,其他線程就可以訪問(wèn)了。 1。
public ThreadTest implements Runnable 2。{ 3。
public synchronized void run(){ 4。for(int i=0;i out。
println(" " + i); 7。} 8。
} 9。public static void main(String[] args) 10。
{ 11。Runnable r1 = new ThreadTest(); 12。
Runnable r2 = new ThreadTest(); 13。 Thread t1 = new Thread(r1); 14。
Thread t2 = new Thread(r2); 15。t1。
start(); 16。t2。
start(); 17。} 18。
} 以上這段程序中的 i 變量并不是共享數(shù)據(jù),也就是這里的Synchronized關(guān)鍵字并未起作用。 因?yàn)閠1,t2兩個(gè)線程是兩個(gè)對(duì)象(r1,r2)的線程。
不同的對(duì)象其數(shù)據(jù)是不同的,所以r1和r2兩個(gè)對(duì)象的i變量是并不是共享數(shù)據(jù)。 當(dāng)把代碼改成如下:Synchronized關(guān)鍵字才會(huì)起作用 19。
Runnable r = new ThreadTest(); 20。 Thread t1 = new Thread(r); 21。
Thread t2 = new Thread(r); 22。t1。
start(); 23。t2。
start(); 以上就是對(duì)Java多線程程序的詳細(xì)介紹。
為什么會(huì)排隊(duì)等待? 下面的這個(gè)簡(jiǎn)單的 Java 程序完成四項(xiàng)不相關(guān)的任務(wù)。
這樣的程序有單個(gè)控制線程,控制在這四個(gè)任務(wù)之間線性地移動(dòng)。此外,因?yàn)樗璧馁Y源 — 打印機(jī)、磁盤(pán)、數(shù)據(jù)庫(kù)和顯示屏 -- 由于硬件和軟件的限制都有內(nèi)在的潛伏時(shí)間,所以每項(xiàng)任務(wù)都包含明顯的等待時(shí)間。
因此,程序在訪問(wèn)數(shù)據(jù)庫(kù)之前必須等待打印機(jī)完成打印文件的任務(wù),等等。如果您正在等待程序的完成,則這是對(duì)計(jì)算資源和您的時(shí)間的一種拙劣使用。
改進(jìn)此程序的一種方法是使它成為多線程的。 在本例中,每項(xiàng)任務(wù)在開(kāi)始之前必須等待前一項(xiàng)任務(wù)完成,即使所涉及的任務(wù)毫不相關(guān)也是這樣。
但是,在現(xiàn)實(shí)生活中,我們經(jīng)常使用多線程模型。我們?cè)谔幚砟承┤蝿?wù)的同時(shí)也可以讓孩子、配偶和父母完成別的任務(wù)。
例如,我在寫(xiě)信的同時(shí)可能打發(fā)我的兒子去郵局買(mǎi)郵票。用軟件術(shù)語(yǔ)來(lái)說(shuō),這稱為多個(gè)控制(或執(zhí)行)線程。
限制線程優(yōu)先級(jí)和調(diào)度 Java 線程模型涉及可以動(dòng)態(tài)更改的線程優(yōu)先級(jí)。
本質(zhì)上,線程的優(yōu)先級(jí)是從 1 到 10 之間的一個(gè)數(shù)字,數(shù)字越大表明任務(wù)越緊急。JVM 標(biāo)準(zhǔn)首先調(diào)用優(yōu)先級(jí)較高的線程,然后才調(diào)用優(yōu)先級(jí)較低的線程。
但是,該標(biāo)準(zhǔn)對(duì)具有相同優(yōu)先級(jí)的線程的處理是隨機(jī)的。 如何處理這些線程取決于基層的操作系統(tǒng)策略。
在某些情況下,優(yōu)先級(jí)相同的線程分時(shí)運(yùn)行;在另一些情況下,線程將一直運(yùn)行到結(jié)束。請(qǐng)記住,Java 支持 10 個(gè)優(yōu)先級(jí),基層操作系統(tǒng)支持的優(yōu)先級(jí)可能要少得多,這樣會(huì)造成一些混亂。
因此,只能將優(yōu)先級(jí)作為一種很粗略的工具使用。 最后的控制可以通過(guò)明智地使用 yield() 函數(shù)來(lái)完成。
通常情況下,請(qǐng)不要依靠線程優(yōu)先級(jí)來(lái)控制線程的狀態(tài)。 小結(jié) 本文說(shuō)明了在 Java 程序中如何使用線程。
像是否應(yīng)該使用線程這樣的更重要的問(wèn)題在很大程序上取決于手頭的應(yīng)用程序。決定是否在應(yīng)用程序中使用多線程的一種方法是,估計(jì)可以并行運(yùn)行的代碼量。
并記住以下幾點(diǎn): 使用多線程不會(huì)增加 CPU 的能力。但是如果使用 JVM 的本地線程實(shí)現(xiàn),則不同的線程可以在不同的處理器上同時(shí)運(yùn)行(在多 CPU 的機(jī)器中),從而使多 CPU 機(jī)器得到充分利用。
如果應(yīng)用程序是計(jì)算密集型的,并受 CPU 功能的制約,則只有多 CPU 機(jī)器能夠從更多的線程中受益。 當(dāng)應(yīng)用程序必須等待緩慢的資源(如網(wǎng)絡(luò)連接或數(shù)據(jù)庫(kù)連接)時(shí),或者當(dāng)應(yīng)用程序是非交互式的時(shí),多線程通常是有利的。
基于 Internet 的軟件有必要是多線程的;否則,用戶將感覺(jué)應(yīng)用程序反映遲鈍。例如,當(dāng)開(kāi)發(fā)要支持大量客戶機(jī)的服務(wù)器時(shí),多線程可以使編程較為容易。
在這種情況下,每個(gè)線程可以為不同的客戶或客戶組服務(wù),從而縮短了響應(yīng)時(shí)間。 某些程序員可能在 C 和其他語(yǔ)言中使用過(guò)線程,在那些語(yǔ)言中對(duì)線程沒(méi)有語(yǔ)言支持。
這些程序員可能通常都被搞得對(duì)線程失去了信心。
一、主內(nèi)存與工作內(nèi)存 1。
Java 內(nèi)存模型的主要目標(biāo)是定義程序中各個(gè)變量的訪問(wèn)規(guī)則。此處的變量與Java編程時(shí)所說(shuō)的變量不一樣,指包括了實(shí)例字段、靜態(tài)字段和構(gòu)成數(shù)組對(duì)象的元素,但是不包括局部變量與方法參數(shù),因?yàn)樗鼈兪蔷€程私有的,不會(huì)被共享。
2。Java內(nèi)存模型中規(guī)定了所有的變量都存儲(chǔ)在主內(nèi)存中,每條線程還有自己的虛擬內(nèi)存。
線程的虛擬內(nèi)存中保存了該線程使用到的變量到主內(nèi)存副本拷貝。線程對(duì)變量的所有操作(讀取、賦值)都必須在自己的虛擬內(nèi)存中進(jìn)行,而不能直接讀寫(xiě)主內(nèi)存中的變量。
不同線程之間無(wú)法直接訪問(wèn)對(duì)方虛擬內(nèi)存中的變量,線程間變量值的傳遞均需要在主內(nèi)存來(lái)完成。 二、內(nèi)存間交互操作 關(guān)于主內(nèi)存與工作內(nèi)存之間的具體交互協(xié)議,即一個(gè)變量如何從主內(nèi)存拷貝到工作內(nèi)存、如何從工作內(nèi)存同步到主內(nèi)存之間的實(shí)現(xiàn)細(xì)節(jié),Java內(nèi)存模型定義了以下八種操作來(lái)完成: ? lock(鎖定):作用于主內(nèi)存的變量,把一個(gè)變量標(biāo)識(shí)為一條線程獨(dú)占狀態(tài)。
? unlock(解鎖):作用于主內(nèi)存變量,把一個(gè)處于鎖定狀態(tài)的變量釋放出來(lái),釋放后 的變量才可以被其他線程鎖定。 。
線程是Java語(yǔ)言的一個(gè)部分,而且是Java的最強(qiáng)大的功能之一。
究竟什么是線程,為什么要開(kāi)發(fā)基于線程的應(yīng)用程序?在本文中,我們將深入了解一下線程的用法,以及使用線程的一些技術(shù)。在我們開(kāi)始講述線程之前,最好先了解一下有關(guān)背景知識(shí)和分析一下線程的工作原理。
當(dāng)程序員一開(kāi)始開(kāi)發(fā)應(yīng)用程序時(shí),這些應(yīng)用程序只能在一個(gè)時(shí)間內(nèi)完成一件事情。應(yīng)用程序從主程序開(kāi)始執(zhí)行,直到運(yùn)行結(jié)束,像 Fortran/Cobol/Basic這些語(yǔ)言均是如此。
隨著時(shí)間的推移,計(jì)算機(jī)發(fā)展到可以在同一時(shí)間段內(nèi)運(yùn)行不止一個(gè)應(yīng)用程序的時(shí)代了,但是應(yīng)用程序運(yùn)行時(shí)仍然是串行的,即從開(kāi)始運(yùn)行到結(jié)束,下一條指令接著上一條指令執(zhí)行。 到最近,程序發(fā)展到可以在執(zhí)行時(shí),以若干個(gè)線程的形式運(yùn)行。
Java就具有運(yùn)行多線程的能力,可以在同一時(shí)間段內(nèi)進(jìn)行幾個(gè)操作,這就意味著給定的操作不必等到另外一個(gè)操作結(jié)束之后,才能開(kāi)始。而且對(duì)某個(gè)操作可以指定更高一級(jí)的優(yōu)先級(jí)。
不少程序語(yǔ)言,包括ADA, Modula-2和C/C++,已經(jīng)可以提供對(duì)線程的支持。 同這些語(yǔ)言相比,Java的特點(diǎn)是從最底層開(kāi)始就對(duì)線程提供支持。
除此以外,標(biāo)準(zhǔn)的Java類是可重入的,允許在一個(gè)給定的應(yīng)用程序中由多個(gè)線程調(diào)用同一方法,而線程彼此之間又互不干擾。Java的這些特點(diǎn)為多線程應(yīng)用程序的設(shè)計(jì)奠定了基礎(chǔ)。
什么是線程?究竟什么是線程呢?正如在圖A中所示,一個(gè)線程是給定的指令的序列 (你所編寫(xiě)的代碼),一個(gè)棧(在給定的方法中定義的變量),以及一些共享數(shù)據(jù)(類一級(jí)的變量)。 線程也可以從全局類中訪問(wèn)靜態(tài)數(shù)據(jù)。
進(jìn)程是程序在處理機(jī)中的一次運(yùn)行。
一個(gè)進(jìn)程既包括其所要執(zhí)行的指令,也包括了執(zhí)行指令所需的系統(tǒng)資源,不同進(jìn)程所占用的系統(tǒng)資源相對(duì)獨(dú)立。所以進(jìn)程是重量級(jí)的任務(wù),它們之間的通信和轉(zhuǎn)換都需要操作系統(tǒng)付出較大的開(kāi)銷。
線程是進(jìn)程中的一個(gè)實(shí)體,是被系統(tǒng)獨(dú)立調(diào)度和分派的基本單位。線程自己基本上不擁有系統(tǒng)資源,但它可以與同屬一個(gè)進(jìn)程的其他線程共享進(jìn)程所擁有的全部資源。
所以線程是輕量級(jí)的任務(wù),它們之間的通信和轉(zhuǎn)換只需要較小的系統(tǒng)開(kāi)銷。 Java支持多線程編程,因此用Java編寫(xiě)的應(yīng)用程序可以同時(shí)執(zhí)行多個(gè)任務(wù)。
Java的多線程機(jī)制使用起來(lái)非常方便,用戶只需關(guān)注程序細(xì)節(jié)的實(shí)現(xiàn),而不用擔(dān)心后臺(tái)的多任務(wù)系統(tǒng)。 Java語(yǔ)言里,線程表現(xiàn)為線程類。
Thread線程類封裝了所有需要的線程操作控制。在設(shè)計(jì)程序時(shí),必須很清晰地區(qū)分開(kāi)線程對(duì)象和運(yùn)行線程,可以將線程對(duì)象看作是運(yùn)行線程的控制面板。
在線程對(duì)象里有很多方法來(lái)控制一個(gè)線程是否運(yùn)行,睡眠,掛起或停止。線程類是控制線程行為的唯一的手段。
一旦一個(gè)Java程序啟動(dòng)后,就已經(jīng)有一個(gè)線程在運(yùn)行??赏ㄟ^(guò)調(diào)用Thread.currentThread方法來(lái)查看當(dāng)前運(yùn)行的是哪一個(gè)線程。
class ThreadTest{ public static void main(String args[]){ Thread t = Thread.currentThread(); t.setName("單線程"); //對(duì)線程取名為"單線程" t.setPriority(8); //設(shè)置線程優(yōu)先級(jí)為8,最高為10,最低為1,默認(rèn)為5 System.out.println("The running thread: " + t); // 顯示線程信息 try{ for(int i=0;i<3;i++){ System.out.println("Sleep time " + i); Thread.sleep(100); // 睡眠100毫秒 } }catch(InterruptedException e){// 捕獲異常 System.out.println("thread has wrong"); } } } 多線程的實(shí)現(xiàn)方法 繼承Thread類 可通過(guò)繼承Thread類并重寫(xiě)其中的run()方法來(lái)定義線程體以實(shí)現(xiàn)線程的具體行為,然后創(chuàng)建該子類的對(duì)象以創(chuàng)建線程。在繼承Thread類的子類ThreadSubclassName中重寫(xiě)run()方法來(lái)定義線程體的一般格式為: public class ThreadSubclassName extends Thread{ public ThreadSubclassName(){ 。
.. // 編寫(xiě)子類的構(gòu)造方法,可缺省 } public void run(){ 。.. // 編寫(xiě)自己的線程代碼 } } 用定義的線程子類ThreadSubclassName創(chuàng)建線程對(duì)象的一般格式為: ThreadSubclassName ThreadObject = new ThreadSubclassName(); 然后,就可啟動(dòng)該線程對(duì)象表示的線程: ThreadObject.start(); //啟動(dòng)線程 應(yīng)用繼承類Thread的方法實(shí)現(xiàn)多線程的程序。
本程序創(chuàng)建了三個(gè)單獨(dú)的線程,它們分別打印自己的“Hello World!”。 class ThreadDemo extends Thread{ private String whoami; private int delay; public ThreadDemo(String s,int d){ whoami=s; delay=d; } public void run(){ try{ sleep(delay); }catch(InterruptedException e){ } System.out.println("Hello World!" + whoami + " " + delay); } } public class MultiThread{ public static void main(String args[]){ ThreadDemo t1,t2,t3; t1 = new ThreadDemo("Thread1", (int)(Math.random()*2000)); t2 = new ThreadDemo("Thread2", (int)(Math.random()*2000)); t3 = new ThreadDemo("Thread3", (int)(Math.random()*2000)); t1.start(); t2.start(); t3.start(); } } 實(shí)現(xiàn)Runnable接口 編寫(xiě)多線程程序的另一種的方法是實(shí)現(xiàn)Runnable接口。
在一個(gè)類中實(shí)現(xiàn)Runnable接口(以后稱實(shí)現(xiàn)Runnable接口的類為Runnable類),并在該類中定義run()方法,然后用帶有Runnable參數(shù)的Thread類構(gòu)造方法創(chuàng)建線程。創(chuàng)建線程對(duì)象可用下面的兩個(gè)步驟來(lái)完成:(1)生成Runnable類ClassName的對(duì)象 ClassName RunnableObject = new ClassName();(2)用帶有Runnable參數(shù)的Thread類構(gòu)造方法創(chuàng)建線程對(duì)象。
新創(chuàng)建的線程的指針將指向Runnable類的實(shí)例。用該Runnable類的實(shí)例為線程提供 run()方法---線程體。
Thread ThreadObject = new Thread(RunnableObject); 然后,就可啟動(dòng)線程對(duì)象ThreadObject表示的線程:ThreadObject.start(); 在Thread類中帶有Runnable接口的構(gòu)造方法有: public Thread(Runnable target); public Thread(Runnable target, String name); public Thread(String name); public Thread(ThreadGroup group,Runnable target); public Thread(ThreadGroup group,Runnable target, String name); 其中,參數(shù)Runnable target表示該線程執(zhí)行時(shí)運(yùn)行target的run()方法,String name以指定名字構(gòu)造線程,ThreadGroup group表示創(chuàng)建線程組。用Runnable接口實(shí)現(xiàn)的多線程。
class TwoThread implements Runnable{ TwoThread(){ Thread t1 = Thread.currentThread(); t1.setName("第一主線程"); System.out.println("正在運(yùn)行的線程: " + t1); Thread t2 = new Thread(this,"第二線程"); System.out.println("創(chuàng)建第二線程"); t2.start(); try{ System.out.println("第一線程休眠"); Thread.sleep(3000); }catch(InterruptedException e){ System.out.println("第一線程有錯(cuò)"); } System.out.println("第一線程退出"); } public void run(){ try{ for(int i = 0;i < 5;i++){ System.out.println(“第二線程的。
聲明:本網(wǎng)站尊重并保護(hù)知識(shí)產(chǎn)權(quán),根據(jù)《信息網(wǎng)絡(luò)傳播權(quán)保護(hù)條例》,如果我們轉(zhuǎn)載的作品侵犯了您的權(quán)利,請(qǐng)?jiān)谝粋€(gè)月內(nèi)通知我們,我們會(huì)及時(shí)刪除。
蜀ICP備2020033479號(hào)-4 Copyright ? 2016 學(xué)習(xí)鳥(niǎo). 頁(yè)面生成時(shí)間:3.112秒