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