Java 里的thread (线程)简介
在Java里 thread 就是線程的意思.
說(shuō)到線程的概念, 自然離不開(kāi)另外兩個(gè)詞: 程序和進(jìn)程.
從最基本的程序講起:
 
一. 什么是程序(Program)
所謂程序, 就是1個(gè)嚴(yán)格有序的指令集合. 程序規(guī)定了完成某一任務(wù)時(shí),計(jì)算機(jī)所需要做的各種操作, 以及操作的順序.
 
1.1 單道程序運(yùn)行環(huán)境
 
 
所謂單道程序環(huán)境就是指, 計(jì)算機(jī)除了操作系統(tǒng)之外, 只允許運(yùn)行1個(gè)用戶程序.
以前的DOS系統(tǒng)就是1個(gè)典型的單道程序運(yùn)行環(huán)境.
 
單道程序有如下特點(diǎn):
1. 資源的獨(dú)占性: 任何時(shí)候, 內(nèi)存內(nèi)只有1個(gè)用戶程序, 這個(gè)程序獨(dú)享系統(tǒng)的所有資源.
2. 執(zhí)行順序性: 內(nèi)存中只執(zhí)行1個(gè)程序, 各程序是按次序執(zhí)行的, 即是做完1件事后, 才做另一件事.
??????????? 絕不可能執(zhí)行1個(gè)程序的中途暫停, 然后去執(zhí)行另1個(gè)程序.
3.結(jié)果的再現(xiàn)性: 只要執(zhí)行環(huán)境和初始條件相同, 重復(fù)執(zhí)行1個(gè)程序, 獲得的結(jié)果總是一樣的.
 
1.2 多道程序運(yùn)行環(huán)境
所謂多道程序運(yùn)行環(huán)境是指, 計(jì)算機(jī)除了操作系統(tǒng)之外, 允許同時(shí)運(yùn)行多個(gè)用戶程序.
當(dāng)今的unix, linux都是典型的多道程序運(yùn)行環(huán)境.
 
多道程序有如下特點(diǎn):
1. 間斷性: 也就是cpu會(huì)在多個(gè)程序之間切換執(zhí)行, 例如cpu執(zhí)行程序A一段時(shí)間后, 會(huì)切換去執(zhí)行程序B一段時(shí)間.
????????????????? 由于這個(gè)時(shí)間段很短, 所以對(duì)用戶造成1個(gè)程序A和程序B同時(shí)執(zhí)行的假象.
 
2. 失去封閉性:? 程序執(zhí)行受外界影響, 也就是多個(gè)程序之間共享資源, 互相制約.
3. 不可再現(xiàn)性:? 重復(fù)執(zhí)行時(shí), 可能得到不同的結(jié)果. 原因就是上面的點(diǎn), 執(zhí)行時(shí)可能會(huì)受到內(nèi)存中其他程序的影響.
二. 什么是進(jìn)程(process)
進(jìn)程這個(gè)概念是基于多道程序運(yùn)行環(huán)境來(lái)講的.? 
 
1個(gè)進(jìn)程就是程序在內(nèi)存中運(yùn)行的1個(gè)實(shí)例
 
由于在多道程序運(yùn)行環(huán)境中, 同1個(gè)程序可以同時(shí)產(chǎn)生兩個(gè)實(shí)例在內(nèi)存里運(yùn)行.
 
舉個(gè)簡(jiǎn)單例子:? 例如你可以在操作系統(tǒng)打開(kāi)兩個(gè)gvim 文本編輯器, 同時(shí)編輯兩個(gè)不同的文件.
這時(shí)執(zhí)行ps -ef | grep gvim 就會(huì)見(jiàn)到系統(tǒng)中有兩個(gè)名字是gvim的進(jìn)程, 但是它們的進(jìn)程id是不同的.
 
也就是將,? gvim這個(gè)程序在內(nèi)存中生成兩個(gè)進(jìn)程同時(shí)運(yùn)行.
 
在多道程序運(yùn)行環(huán)境中.? 引用進(jìn)程這個(gè)概念來(lái)描敘程序在內(nèi)存里事例, 用來(lái)區(qū)別于程序本身.
 
真正深入了解進(jìn)程并不簡(jiǎn)單, 進(jìn)程其實(shí)算是屬于操作系統(tǒng)這門(mén)課的范疇. 1個(gè)進(jìn)程包括程序段, 數(shù)據(jù)段, 程序控制塊等,? 但是作為一個(gè)普通的程序猿, 沒(méi)必要理解得這么深入.
 
 
三. 什么是線程(thread).
相對(duì)地, 我認(rèn)為作為1個(gè)普通的程序猿, 必須深入了解線程這個(gè)概念.
 
其實(shí), 線程就是1個(gè)進(jìn)程的執(zhí)行路徑.
一般的java程序都是從啟動(dòng)類(lèi)的main函數(shù)入口開(kāi)始執(zhí)行, main函數(shù)的結(jié)束而停止.?? 這條執(zhí)行路徑就是java程序的主線程.
 
也就是講:
線程是相對(duì)于進(jìn)程來(lái)講的, 1個(gè)進(jìn)程至少存在1條線程.
 
而java允許多線程編程, 也就是1個(gè)進(jìn)程除主線程外,? 還允許存在1個(gè)或若干個(gè)其他線程, cpu會(huì)在這些線程當(dāng)中間斷切換執(zhí)行, 給用戶造成同時(shí)執(zhí)行的假象.
 
 
四. 1個(gè)單線程java程序的簡(jiǎn)單例子.
例子如下:
package Thread_kng;class S_thrd_1{public void f(){while (true){System.out.printf("Thread main is runing!\n");}//System.out.printf("f() is done!\n"); //compilation fail} }public class S_thread_expl{public static void g(){S_thrd_1 s = new S_thrd_1();s.f(); System.out.printf("g() is done!\n");} }上面的例子中,? g() 函數(shù)調(diào)用了f() 函數(shù),
g()函數(shù)在最后嘗試輸出"g() is done!" , 但是因?yàn)閒() 是一個(gè)無(wú)限循環(huán).? 所以g() 調(diào)用f()后, 根本沒(méi)機(jī)會(huì)執(zhí)行后面的語(yǔ)句!
 
也就是說(shuō), 雖然g() 跟 f() 是兩個(gè)不同類(lèi)的函數(shù), 但是它們是在程序中的同一個(gè)線程(主線程)內(nèi)執(zhí)行.???
在同一個(gè)線程內(nèi), 語(yǔ)句總是按程序猿編寫(xiě)的順序依次執(zhí)行, 一條語(yǔ)句未執(zhí)行完時(shí), 不會(huì)跳到后面執(zhí)行其他的語(yǔ)句.
 
 
五. 1個(gè)多線程java程序的簡(jiǎn)單例子.
例子如下:
package Thread_kng;class M_thrd_1 extends Thread{ //extendspublic M_thrd_1(String name){super(name);}public void run(){ //overwrite the method run() of superclasswhile (true){System.out.printf("Thread " + this.getName()+ " is runing!\n");}//System.out.printf("f() is done!\n"); //compilation fail} }public class M_thread_expl{public static void g(){M_thrd_1 s = new M_thrd_1("T1");s.start(); //start()method is extended from superclass, it will call the method run()M_thrd_1 s2 = new M_thrd_1("T2");s2.start();System.out.printf("g() is done!\n");}
 
上面例子中, 類(lèi) M_thrd_1繼承了線程類(lèi)Thread.? 并重寫(xiě)了run方法.
 
在下面的g()函數(shù)中.
實(shí)例化了兩個(gè)類(lèi)M_thrd_1的對(duì)象s, s1 的對(duì)象. 
 
執(zhí)行了start()方法.
start()方法繼承字Thread, 它會(huì)啟動(dòng)1新線程, 并調(diào)用run()函數(shù).
而g()后面本身也在不斷循環(huán)輸出"THread Main is running"
 
 
所以輸出如下:
 
 
 
 
 
可以 見(jiàn)到, 輸出結(jié)果是T1,T2 和Main 交替輸出在屏幕上.
 
實(shí)際上, 這個(gè)程序有3個(gè)線程.
其中1個(gè)就是主線程.
主線程main通過(guò)實(shí)例化兩個(gè)M_thrd_1的對(duì)象, 調(diào)用其start()函數(shù)開(kāi)啟了兩個(gè)子線程.
其中1個(gè)子線程不斷輸出T1
另1個(gè)子線程不斷輸出T2
 
但是主線程并不會(huì)等待其子線程執(zhí)行完成, 會(huì)繼續(xù)執(zhí)行下面的代碼,所以不斷循環(huán)輸出Main!
 
所以這個(gè)例子實(shí)際上要3個(gè)線程在輸出信息到屏幕了!
 
六. Java 開(kāi)啟一條新進(jìn)程的兩種方式.
java開(kāi)啟一條新線程, 有兩種方式. 但是都要利用java的線程基類(lèi)Thread.
 
6.1 方式1. 創(chuàng)建Thread的派生類(lèi)
 
 
 
這個(gè)就類(lèi)似上面的例子:
具體步驟如下:
 
6.1.1 線程準(zhǔn)備: 新建1個(gè)類(lèi), 繼承線程類(lèi)Thread.? 將業(yè)務(wù)代碼寫(xiě)入重寫(xiě)后的run() 方法中.
例如上面的例子中的M_thrd_1類(lèi)
class M_thrd_1 extends Thread{ //extendspublic M_thrd_1(String name){super(name);}public void run(){ //overwrite the method run() of superclasswhile (true){System.out.printf("Thread " + this.getName()+ " is runing!\n");}//System.out.printf("f() is done!\n"); //compilation fail} }可以見(jiàn)到, 我們將線程要執(zhí)行的業(yè)務(wù)代碼寫(xiě)入了run() 方法.
 
 
6.1.2 啟動(dòng)線程: 新建1個(gè)上述類(lèi)的對(duì)象, 運(yùn)行start()方法
例如
M_thrd_1 s = new M_thrd();
s. start();
 
其中start() 是類(lèi)M_thrd_1 繼承自超類(lèi)Thread的.
我們看看JDK API 對(duì)start() 方法的定義:
 
?void? start()
 ??????????使該線程開(kāi)始執(zhí)行;Java 虛擬機(jī)調(diào)用該線程的 run 方法。
 
可以見(jiàn)到, 調(diào)用了start() 方法后, 會(huì)開(kāi)始執(zhí)行改線程, 也就是在當(dāng)前線程下開(kāi)啟一條新線程.
而這條新線程做什么呢, 就是執(zhí)行run()里面的代碼.
 
所以我們一般只需要把也業(yè)務(wù)的代碼寫(xiě)入run()里面就ok了. 不要重寫(xiě)start()方法.
 
 
注意, 如果直接執(zhí)行s.run(); 則會(huì)在當(dāng)前線程下執(zhí)行run里面的代碼, 并沒(méi)有開(kāi)啟一條新線程. 區(qū)別很大的.
 
 
6.2 方式2. 創(chuàng)建1個(gè)類(lèi), 實(shí)現(xiàn)Runnable 接口
步驟跟上面的方式差別不大
 
6.2.1 線程準(zhǔn)備: 新建1個(gè)類(lèi), 實(shí)現(xiàn)接口Runnable. 將業(yè)務(wù)代碼寫(xiě)入重寫(xiě)后的run() 方法中.
例子:
class M_thrd_2 implements Runnable{ //extendspublic int id = 0;public void run(){ //overwrite the method run() of superclasswhile (true){System.out.printf("%s: Thread " + this.id+ " is runing!\n", Thread.currentThread().getName());}//System.out.printf("f() is done!\n"); //compilation fail} }上面的例子 M_thrd_2 就實(shí)現(xiàn)了接口Runnable
并重寫(xiě)了接口Runnable 的抽象方法run();
 
注意.? currentThread() 是類(lèi)Thread的一個(gè)靜態(tài)方法, 作用是獲取當(dāng)前語(yǔ)句所在的線程.
 
6.2.2 啟動(dòng)線程: 新建1個(gè)上述類(lèi)的對(duì)象s, 在新建1個(gè)類(lèi)Thread的對(duì)象t, 把s作為一個(gè)參數(shù)用于t的構(gòu)造方法.? 并執(zhí)行t.start()
貌似比方式一復(fù)雜.
其實(shí)非如下:
new Thread(new M_thrd_2()).start()
 
例子:
public class M_thread_expl2{public static void g(){M_thrd_2 s = new M_thrd_2();s.id = 1;Thread t1 = new Thread(s);t1.start();Thread t2 = new Thread(s);s.id = 2;t2.start();} }在g() 函數(shù)中,
首先新建1個(gè)類(lèi)M_thrd_2的 對(duì)象s.
并利用s作為參數(shù) 建立了兩個(gè)線程類(lèi)Thread的對(duì)象t1 和 t2. 并啟動(dòng)這兩個(gè)線程.
 
注:
1. 這個(gè)例子中有3個(gè)線程, 其中1個(gè)主線程(也就是g() 函數(shù)所在的線程). 開(kāi)啟了兩個(gè)子線程, 該兩個(gè)子線程都在不斷循環(huán)輸出信息到屏幕上.
2. Thread(object ob) 是1個(gè)屬于類(lèi)Thread的構(gòu)造方法,? 其中的對(duì)象ob 必須實(shí)現(xiàn)Runnable 接口.
3. 這個(gè)例子執(zhí)行時(shí) , 第一個(gè)t1首先會(huì)輸出id = 1,??? 但是當(dāng)?shù)诙€(gè)線程t2開(kāi)始執(zhí)行后, t1會(huì)輸出id=2,? 因?yàn)閠1, 和t2都是利用同1個(gè)對(duì)象建立的.
??? 也就是說(shuō), 這個(gè)對(duì)象的變動(dòng)會(huì)同時(shí)影響兩個(gè)線程.
 
輸出如下:
 
 
 
6.3 兩種方式的區(qū)別
1.? 方式1是創(chuàng)建繼承類(lèi)Thread的派生類(lèi),? 方式2是創(chuàng)建實(shí)現(xiàn)Runnable接口的類(lèi).
2.? 啟動(dòng)方式:? 方式1是直接調(diào)用業(yè)務(wù)類(lèi)的對(duì)象的start()方法, 方式2是利用業(yè)務(wù)類(lèi)類(lèi)的對(duì)象新建1個(gè)類(lèi)Thread的對(duì)象, 并調(diào)用該對(duì)象的start()方法.
3.? 如果要啟動(dòng)多個(gè)線程, 通過(guò)方式1需要新建多個(gè)不同業(yè)務(wù)類(lèi)的對(duì)象, 方式2 則可以通過(guò)業(yè)務(wù)1個(gè)對(duì)象 構(gòu)建多個(gè)Thread類(lèi)對(duì)象, 但是業(yè)務(wù)對(duì)象的變動(dòng)會(huì)同時(shí)影響對(duì)應(yīng)的多個(gè)線程.
 
 
七. 關(guān)于線程的一些常用方法介紹.
7.1 run()
我們要吧線把要執(zhí)行的業(yè)務(wù)代碼寫(xiě)入這個(gè)方法. 上面提過(guò)了.? 注意, 如果我們直接執(zhí)行1個(gè)線程對(duì)象的run()方法可以合法的, 但是仍然是在當(dāng)前線程內(nèi)執(zhí)行, 并沒(méi)有開(kāi)始一條新線程.
 
7.2 Start()
當(dāng)1個(gè)線程or其派生類(lèi)執(zhí)行1個(gè)start()方法. 就開(kāi)啟1個(gè)新線程, 并調(diào)用該類(lèi)的run()方法.
注意: 1個(gè)線程如果已經(jīng)調(diào)用過(guò)一次start(), 再調(diào)用start()則或拋異常.
 
7.3 stop()
停止1個(gè)1個(gè)線程.
 
7.4 setName() 和 getName()
設(shè)置和獲得對(duì)應(yīng)線程的名字.
 
7.5 Thread.currentThread()
注意這個(gè)方法是一個(gè)static方法,? 而上面的都不是靜態(tài)方法.
 
這個(gè)方法返回當(dāng)前執(zhí)行語(yǔ)句所屬的線程.
 
例如1個(gè)線程對(duì)象A.??? 它的run() 方法里調(diào)用了 Thread.currentThread().getName() 函數(shù).
 
假如直接執(zhí)行A.run()? 則返回的是當(dāng)前線程(而不是A線程) 的名字,? 因?yàn)閷?duì)象A并沒(méi)有啟動(dòng)自己的線程.
假如執(zhí)行難過(guò)A.start(), 那么該函數(shù)就返回A啟動(dòng)的線程的名字了.
 
 
八, 小結(jié)
這篇文章只是簡(jiǎn)介, 很多重要的方法例如 sleep() , wait() 等都沒(méi)有介紹, 這些是線程控制的內(nèi)容. 本吊打算在另一篇博文里介紹.
 
 
 
 
 
 
 
 
 
 
總結(jié)
以上是生活随笔為你收集整理的Java 里的thread (线程)简介的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
 
                            
                        - 上一篇: jar包的生成和使用简单例子
- 下一篇: Java里的线程控制
