原帖地址:http://lavasoft.blog.51cto.com/62575/27069
Java多線(xiàn)程編程總結(jié)
-------------------------------------------------------------------------------------------------
下面的內(nèi)容是很早之前寫(xiě)的,內(nèi)容不夠充實(shí),而且是基于Java1.4的內(nèi)容,Java5之后,線(xiàn)程并發(fā)部分?jǐn)U展了相當(dāng)多的內(nèi)容,因此建議大家看上面的系列文章的內(nèi)容,與時(shí)俱進(jìn),跟上Java發(fā)展的步伐。
-------------------------------------------------------------------------------------------------
一、認(rèn)識(shí)多任務(wù)、多進(jìn)程、單線(xiàn)程、多線(xiàn)程
要認(rèn)識(shí)多線(xiàn)程就要從操作系統(tǒng)的原理說(shuō)起。
以前古老的DOS操作系統(tǒng)(V 6.22)是單任務(wù)的,還沒(méi)有線(xiàn)程的概念,系統(tǒng)在每次只能做一件事情。比如你在copy東西的時(shí)候不能rename文件名。為了提高系統(tǒng)的利用效率,采用批處理來(lái)批量執(zhí)行任務(wù)。
現(xiàn)在的操作系統(tǒng)都是多任務(wù)操作系統(tǒng),每個(gè)運(yùn)行的任務(wù)就是操作系統(tǒng)所做的一件事情,比如你在聽(tīng)歌的同時(shí)還在用MSN和好友聊天。聽(tīng)歌和聊天就是兩個(gè)任務(wù),這個(gè)兩個(gè)任務(wù)是“同時(shí)”進(jìn)行的。一個(gè)任務(wù)一般對(duì)應(yīng)一個(gè)進(jìn)程,也可能包含好幾個(gè)進(jìn)程。比如運(yùn)行的MSN就對(duì)應(yīng)一個(gè)MSN的進(jìn)程,如果你用的是 windows系統(tǒng),你就可以在任務(wù)管理器中看到操作系統(tǒng)正在運(yùn)行的進(jìn)程信息。
一般來(lái)說(shuō),當(dāng)運(yùn)行一個(gè)應(yīng)用程序的時(shí)候,就啟動(dòng)了一個(gè)進(jìn)程,當(dāng)然有些會(huì)啟動(dòng)多個(gè)進(jìn)程。啟動(dòng)進(jìn)程的時(shí)候,操作系統(tǒng)會(huì)為進(jìn)程分配資源,其中最主要的資源是內(nèi)存空間,因?yàn)槌绦蚴窃趦?nèi)存中運(yùn)行的。在進(jìn)程中,有些程序流程塊是可以亂序執(zhí)行的,并且這個(gè)代碼塊可以同時(shí)被多次執(zhí)行。實(shí)際上,這樣的代碼塊就是線(xiàn)程體。線(xiàn)程是進(jìn)程中亂序執(zhí)行的代碼流程。當(dāng)多個(gè)線(xiàn)程同時(shí)運(yùn)行的時(shí)候,這樣的執(zhí)行模式成為并發(fā)執(zhí)行。
多線(xiàn)程的目的是為了最大限度的利用CPU資源。
Java編寫(xiě)程序都運(yùn)行在在Java虛擬機(jī)(JVM)中,在JVM的內(nèi)部,程序的多任務(wù)是通過(guò)線(xiàn)程來(lái)實(shí)現(xiàn)的。每用java命令啟動(dòng)一個(gè)java 應(yīng)用程序,就會(huì)啟動(dòng)一個(gè)JVM進(jìn)程。在同一個(gè)JVM進(jìn)程中,有且只有一個(gè)進(jìn)程,就是它自己。在這個(gè)JVM環(huán)境中,所有程序代碼的運(yùn)行都是以線(xiàn)程來(lái)運(yùn)行。
一般常見(jiàn)的Java應(yīng)用程序都是單線(xiàn)程的。比如,用java命令運(yùn)行一個(gè)最簡(jiǎn)單的HelloWorld的Java應(yīng)用程序時(shí),就啟動(dòng)了一個(gè) JVM進(jìn)程,JVM找到程序程序的入口點(diǎn)main(),然后運(yùn)行main()方法,這樣就產(chǎn)生了一個(gè)線(xiàn)程,這個(gè)線(xiàn)程稱(chēng)之為主線(xiàn)程。當(dāng)main方法結(jié)束后,主線(xiàn)程運(yùn)行完成。JVM進(jìn)程也隨即退出 。
對(duì)于一個(gè)進(jìn)程中的多個(gè)線(xiàn)程來(lái)說(shuō),多個(gè)線(xiàn)程共享進(jìn)程的內(nèi)存塊,當(dāng)有新的線(xiàn)程產(chǎn)生的時(shí)候,操作系統(tǒng)不分配新的內(nèi)存,而是讓新線(xiàn)程共享原有的進(jìn)程塊的內(nèi)存。因此,線(xiàn)程間的通信很容易,速度也很快。不同的進(jìn)程因?yàn)樘幱诓煌膬?nèi)存塊,因此進(jìn)程之間的通信相對(duì)困難。
實(shí)際上,操作的系統(tǒng)的多進(jìn)程實(shí)現(xiàn)了多任務(wù)并發(fā)執(zhí)行,程序的多線(xiàn)程實(shí)現(xiàn)了進(jìn)程的并發(fā)執(zhí)行。多任務(wù)、多進(jìn)程、多線(xiàn)程的前提都是要求操作系統(tǒng)提供多任務(wù)、多進(jìn)程、多線(xiàn)程的支持。
在Java程序中,JVM負(fù)責(zé)線(xiàn)程的調(diào)度。線(xiàn)程調(diào)度是值按照特定的機(jī)制為多個(gè)線(xiàn)程分配CPU的使用權(quán)。
調(diào)度的模式有兩種:分時(shí)調(diào)度和搶占式調(diào)度。分時(shí)調(diào)度是所有線(xiàn)程輪流獲得CPU使用權(quán),并平均分配每個(gè)線(xiàn)程占用CPU的時(shí)間;搶占式調(diào)度是根據(jù)線(xiàn)程的優(yōu)先級(jí)別來(lái)獲取CPU的使用權(quán)。JVM的線(xiàn)程調(diào)度模式采用了搶占式模式。
所謂的“并發(fā)執(zhí)行”、“同時(shí)”其實(shí)都不是真正意義上的“同時(shí)”。眾所周知,CPU都有個(gè)時(shí)鐘頻率,表示每秒中能執(zhí)行cpu指令的次數(shù)。在每個(gè)時(shí)鐘周期內(nèi),CPU實(shí)際上只能去執(zhí)行一條(也有可能多條)指令。操作系統(tǒng)將進(jìn)程線(xiàn)程進(jìn)行管理,輪流(沒(méi)有固定的順序)分配每個(gè)進(jìn)程很短的一段是時(shí)間(不一定是均分),然后在每個(gè)線(xiàn)程內(nèi)部,程序代碼自己處理該進(jìn)程內(nèi)部線(xiàn)程的時(shí)間分配,多個(gè)線(xiàn)程之間相互的切換去執(zhí)行,這個(gè)切換時(shí)間也是非常短的。因此多任務(wù)、多進(jìn)程、多線(xiàn)程都是操作系統(tǒng)給人的一種宏觀感受,從微觀角度看,程序的運(yùn)行是異步執(zhí)行的。
用一句話(huà)做總結(jié):雖然操作系統(tǒng)是多線(xiàn)程的,但CPU每一時(shí)刻只能做一件事,和人的大腦是一樣的,呵呵。
二、Java與多線(xiàn)程
Java語(yǔ)言的多線(xiàn)程需要操作系統(tǒng)的支持。
Java 虛擬機(jī)允許應(yīng)用程序并發(fā)地運(yùn)行多個(gè)執(zhí)行線(xiàn)程。Java語(yǔ)言提供了多線(xiàn)程編程的擴(kuò)展點(diǎn),并給出了功能強(qiáng)大的線(xiàn)程控制API。
在Java中,多線(xiàn)程的實(shí)現(xiàn)有兩種方式:
擴(kuò)展java.lang.Thread類(lèi)
實(shí)現(xiàn)java.lang.Runnable接口
每個(gè)線(xiàn)程都有一個(gè)優(yōu)先級(jí),高優(yōu)先級(jí)線(xiàn)程的執(zhí)行優(yōu)先于低優(yōu)先級(jí)線(xiàn)程。每個(gè)線(xiàn)程都可以或不可以標(biāo)記為一個(gè)守護(hù)程序。當(dāng)某個(gè)線(xiàn)程中運(yùn)行的代碼創(chuàng)建一個(gè)新 Thread 對(duì)象時(shí),該新線(xiàn)程的初始優(yōu)先級(jí)被設(shè)定為創(chuàng)建線(xiàn)程的優(yōu)先級(jí),并且當(dāng)且僅當(dāng)創(chuàng)建線(xiàn)程是守護(hù)線(xiàn)程時(shí),新線(xiàn)程才是守護(hù)程序。
當(dāng) Java 虛擬機(jī)啟動(dòng)時(shí),通常都會(huì)有單個(gè)非守護(hù)線(xiàn)程(它通常會(huì)調(diào)用某個(gè)指定類(lèi)的 main 方法)。Java 虛擬機(jī)會(huì)繼續(xù)執(zhí)行線(xiàn)程,直到下列任一情況出現(xiàn)時(shí)為止:
調(diào)用了 Runtime 類(lèi)的 exit 方法,并且安全管理器允許退出操作發(fā)生。
非守護(hù)線(xiàn)程的所有線(xiàn)程都已停止運(yùn)行,無(wú)論是通過(guò)從對(duì) run 方法的調(diào)用中返回,還是通過(guò)拋出一個(gè)傳播到 run 方法之外的異常。
三、擴(kuò)展java.lang.Thread類(lèi)
/**
* File Name: TestMitiThread.java
* Created by: IntelliJ IDEA.
* Copyright: Copyright (c) 2003-2006
* Company: Lavasoft( [url]http://lavasoft.blog.51cto.com/[/url])
* Author: leizhimin
* Modifier: leizhimin
* Date Time: 2007-5-17 10:03:12
* Readme: 通過(guò)擴(kuò)展Thread類(lèi)實(shí)現(xiàn)多線(xiàn)程
*/
public class TestMitiThread {
public static void main(String[] rags) {
System.out.println(Thread.currentThread().getName() + " 線(xiàn)程運(yùn)行開(kāi)始!");
new MitiSay("A").start();
new MitiSay("B").start();
System.out.println(Thread.currentThread().getName() + " 線(xiàn)程運(yùn)行結(jié)束!");
}
}
class MitiSay extends Thread {
public MitiSay(String threadName) {
super(threadName);
}
public void run() {
System.out.println(getName() + " 線(xiàn)程運(yùn)行開(kāi)始!");
for (int i = 0; i < 10; i++) {
System.out.println(i + " " + getName());
try {
sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(getName() + " 線(xiàn)程運(yùn)行結(jié)束!");
}
}
運(yùn)行結(jié)果:
main 線(xiàn)程運(yùn)行開(kāi)始!
main 線(xiàn)程運(yùn)行結(jié)束!
A 線(xiàn)程運(yùn)行開(kāi)始!
0 A
1 A
B 線(xiàn)程運(yùn)行開(kāi)始!
2 A
0 B
3 A
4 A
1 B
5 A
6 A
7 A
8 A
9 A
A 線(xiàn)程運(yùn)行結(jié)束!
2 B
3 B
4 B
5 B
6 B
7 B
8 B
9 B
B 線(xiàn)程運(yùn)行結(jié)束!
說(shuō)明:
程序啟動(dòng)運(yùn)行main時(shí)候,java虛擬機(jī)啟動(dòng)一個(gè)進(jìn)程,主線(xiàn)程main在main()調(diào)用時(shí)候被創(chuàng)建。隨著調(diào)用MitiSay的兩個(gè)對(duì)象的start方法,另外兩個(gè)線(xiàn)程也啟動(dòng)了,這樣,整個(gè)應(yīng)用就在多線(xiàn)程下運(yùn)行。
在一個(gè)方法中調(diào)用Thread.currentThread().getName()方法,可以獲取當(dāng)前線(xiàn)程的名字。在mian方法中調(diào)用該方法,獲取的是主線(xiàn)程的名字。
注意:start()方法的調(diào)用后并不是立即執(zhí)行多線(xiàn)程代碼,而是使得該線(xiàn)程變?yōu)榭蛇\(yùn)行態(tài)(Runnable),什么時(shí)候運(yùn)行是由操作系統(tǒng)決定的。
從程序運(yùn)行的結(jié)果可以發(fā)現(xiàn),多線(xiàn)程程序是亂序執(zhí)行。因此,只有亂序執(zhí)行的代碼才有必要設(shè)計(jì)為多線(xiàn)程。
Thread.sleep()方法調(diào)用目的是不讓當(dāng)前線(xiàn)程獨(dú)自霸占該進(jìn)程所獲取的CPU資源,以留出一定時(shí)間給其他線(xiàn)程執(zhí)行的機(jī)會(huì)。
實(shí)際上所有的多線(xiàn)程代碼執(zhí)行順序都是不確定的,每次執(zhí)行的結(jié)果都是隨機(jī)的。
四、實(shí)現(xiàn)java.lang.Runnable接口
/**
* 通過(guò)實(shí)現(xiàn) Runnable 接口實(shí)現(xiàn)多線(xiàn)程
*/
public class TestMitiThread1 implements Runnable {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + " 線(xiàn)程運(yùn)行開(kāi)始!");
TestMitiThread1 test = new TestMitiThread1();
Thread thread1 = new Thread(test);
Thread thread2 = new Thread(test);
thread1.start();
thread2.start();
System.out.println(Thread.currentThread().getName() + " 線(xiàn)程運(yùn)行結(jié)束!");
}
public void run() {
System.out.println(Thread.currentThread().getName() + " 線(xiàn)程運(yùn)行開(kāi)始!");
for (int i = 0; i < 10; i++) {
System.out.println(i + " " + Thread.currentThread().getName());
try {
Thread.sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 線(xiàn)程運(yùn)行結(jié)束!");
}
}
運(yùn)行結(jié)果:
main 線(xiàn)程運(yùn)行開(kāi)始!
Thread-0 線(xiàn)程運(yùn)行開(kāi)始!
main 線(xiàn)程運(yùn)行結(jié)束!
0 Thread-0
Thread-1 線(xiàn)程運(yùn)行開(kāi)始!
0 Thread-1
1 Thread-1
1 Thread-0
2 Thread-0
2 Thread-1
3 Thread-0
3 Thread-1
4 Thread-0
4 Thread-1
5 Thread-0
6 Thread-0
5 Thread-1
7 Thread-0
8 Thread-0
6 Thread-1
9 Thread-0
7 Thread-1
Thread-0 線(xiàn)程運(yùn)行結(jié)束!
8 Thread-1
9 Thread-1
Thread-1 線(xiàn)程運(yùn)行結(jié)束!
說(shuō)明:
TestMitiThread1類(lèi)通過(guò)實(shí)現(xiàn)Runnable接口,使得該類(lèi)有了多線(xiàn)程類(lèi)的特征。run()方法是多線(xiàn)程程序的一個(gè)約定。所有的多線(xiàn)程代碼都在run方法里面。Thread類(lèi)實(shí)際上也是實(shí)現(xiàn)了Runnable接口的類(lèi)。
在啟動(dòng)的多線(xiàn)程的時(shí)候,需要先通過(guò)Thread類(lèi)的構(gòu)造方法Thread(Runnable target) 構(gòu)造出對(duì)象,然后調(diào)用Thread對(duì)象的start()方法來(lái)運(yùn)行多線(xiàn)程代碼。
實(shí)際上所有的多線(xiàn)程代碼都是通過(guò)運(yùn)行Thread的start()方法來(lái)運(yùn)行的。因此,不管是擴(kuò)展Thread類(lèi)還是實(shí)現(xiàn)Runnable接口來(lái)實(shí)現(xiàn)多線(xiàn)程,最終還是通過(guò)Thread的對(duì)象的API來(lái)控制線(xiàn)程的,熟悉Thread類(lèi)的API是進(jìn)行多線(xiàn)程編程的基礎(chǔ)。
五、讀解Thread類(lèi)API
static int MAX_PRIORITY
線(xiàn)程可以具有的最高優(yōu)先級(jí)。
static int MIN_PRIORITY
線(xiàn)程可以具有的最低優(yōu)先級(jí)。
static int NORM_PRIORITY
分配給線(xiàn)程的默認(rèn)優(yōu)先級(jí)。
構(gòu)造方法摘要
Thread(Runnable target)
分配新的 Thread 對(duì)象。
Thread(String name)
分配新的 Thread 對(duì)象。
方法摘要
static Thread currentThread()
返回對(duì)當(dāng)前正在執(zhí)行的線(xiàn)程對(duì)象的引用。
ClassLoader getContextClassLoader()
返回該線(xiàn)程的上下文 ClassLoader。
long getId()
返回該線(xiàn)程的標(biāo)識(shí)符。
String getName()
返回該線(xiàn)程的名稱(chēng)。
int getPriority()
返回線(xiàn)程的優(yōu)先級(jí)。
Thread.State getState()
返回該線(xiàn)程的狀態(tài)。
ThreadGroup getThreadGroup()
返回該線(xiàn)程所屬的線(xiàn)程組。
static boolean holdsLock(Object obj)
當(dāng)且僅當(dāng)當(dāng)前線(xiàn)程在指定的對(duì)象上保持監(jiān)視器鎖時(shí),才返回 true。
void interrupt()
中斷線(xiàn)程。
static boolean interrupted()
測(cè)試當(dāng)前線(xiàn)程是否已經(jīng)中斷。
boolean isAlive()
測(cè)試線(xiàn)程是否處于活動(dòng)狀態(tài)。
boolean isDaemon()
測(cè)試該線(xiàn)程是否為守護(hù)線(xiàn)程。
boolean isInterrupted()
測(cè)試線(xiàn)程是否已經(jīng)中斷。
void join()
等待該線(xiàn)程終止。
void join(long millis)
等待該線(xiàn)程終止的時(shí)間最長(zhǎng)為 millis 毫秒。
void join(long millis, int nanos)
等待該線(xiàn)程終止的時(shí)間最長(zhǎng)為 millis 毫秒 + nanos 納秒。
void resume()
已過(guò)時(shí)。 該方法只與 suspend() 一起使用,但 suspend() 已經(jīng)遭到反對(duì),因?yàn)樗哂兴梨i傾向。有關(guān)更多信息,請(qǐng)參閱為何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反對(duì)?。
void run()
如果該線(xiàn)程是使用獨(dú)立的 Runnable 運(yùn)行對(duì)象構(gòu)造的,則調(diào)用該 Runnable 對(duì)象的 run 方法;否則,該方法不執(zhí)行任何操作并返回。
void setContextClassLoader(ClassLoader cl)
設(shè)置該線(xiàn)程的上下文 ClassLoader。
void setDaemon(boolean on)
將該線(xiàn)程標(biāo)記為守護(hù)線(xiàn)程或用戶(hù)線(xiàn)程。
static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
設(shè)置當(dāng)線(xiàn)程由于未捕獲到異常而突然終止,并且沒(méi)有為該線(xiàn)程定義其他處理程序時(shí)所調(diào)用的默認(rèn)處理程序。
void setName(String name)
改變線(xiàn)程名稱(chēng),使之與參數(shù) name 相同。
void setPriority(int newPriority)
更改線(xiàn)程的優(yōu)先級(jí)。
void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
設(shè)置該線(xiàn)程由于未捕獲到異常而突然終止時(shí)調(diào)用的處理程序。
static void sleep(long millis)
在指定的毫秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線(xiàn)程休眠(暫停執(zhí)行)。
static void sleep(long millis, int nanos)
在指定的毫秒數(shù)加指定的納秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線(xiàn)程休眠(暫停執(zhí)行)。
void start()
使該線(xiàn)程開(kāi)始執(zhí)行;Java 虛擬機(jī)調(diào)用該線(xiàn)程的 run 方法。
void stop()
已過(guò)時(shí)。 該方法具有固有的不安全性。用 Thread.stop 來(lái)終止線(xiàn)程將釋放它已經(jīng)鎖定的所有監(jiān)視器(作為沿堆棧向上傳播的未檢查 ThreadDeath 異常的一個(gè)自然后果)。如果以前受這些監(jiān)視器保護(hù)的任何對(duì)象都處于一種不一致的狀態(tài),則損壞的對(duì)象將對(duì)其他線(xiàn)程可見(jiàn),這有可能導(dǎo)致任意的行為。stop 的許多使用都應(yīng)由只修改某些變量以指示目標(biāo)線(xiàn)程應(yīng)該停止運(yùn)行的代碼來(lái)取代。目標(biāo)線(xiàn)程應(yīng)定期檢查該變量,并且如果該變量指示它要停止運(yùn)行,則從其運(yùn)行方法依次返回。如果目標(biāo)線(xiàn)程等待很長(zhǎng)時(shí)間(例如基于一個(gè)條件變量),則應(yīng)使用 interrupt 方法來(lái)中斷該等待。有關(guān)更多信息,請(qǐng)參閱《為何不贊成使用 Thread.stop、Thread.suspend 和 Thread.resume?》。
void stop(Throwable obj)
已過(guò)時(shí)。 該方法具有固有的不安全性。請(qǐng)參閱 stop() 以獲得詳細(xì)信息。該方法的附加危險(xiǎn)是它可用于生成目標(biāo)線(xiàn)程未準(zhǔn)備處理的異常(包括若沒(méi)有該方法該線(xiàn)程不太可能拋出的已檢查的異常)。有關(guān)更多信息,請(qǐng)參閱為何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反對(duì)?。
void suspend()
已過(guò)時(shí)。該方法已經(jīng)遭到反對(duì),因?yàn)樗哂泄逃械乃梨i傾向。如果目標(biāo)線(xiàn)程掛起時(shí)在保護(hù)關(guān)鍵系統(tǒng)資源的監(jiān)視器上保持有鎖,則在目標(biāo)線(xiàn)程重新開(kāi)始以前任何線(xiàn)程都不能訪問(wèn)該資源。如果重新開(kāi)始目標(biāo)線(xiàn)程的線(xiàn)程想在調(diào)用 resume 之前鎖定該監(jiān)視器,則會(huì)發(fā)生死鎖。這類(lèi)死鎖通常會(huì)證明自己是“凍結(jié)”的進(jìn)程。有關(guān)更多信息,請(qǐng)參閱為何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反對(duì)?。
String toString()
返回該線(xiàn)程的字符串表示形式,包括線(xiàn)程名稱(chēng)、優(yōu)先級(jí)和線(xiàn)程組。
static void yield()
暫停當(dāng)前正在執(zhí)行的線(xiàn)程對(duì)象,并執(zhí)行其他線(xiàn)程。
六、線(xiàn)程的狀態(tài)轉(zhuǎn)換圖
線(xiàn)程在一定條件下,狀態(tài)會(huì)發(fā)生變化。線(xiàn)程變化的狀態(tài)轉(zhuǎn)換圖如下:

1、新建狀態(tài)(New):新創(chuàng)建了一個(gè)線(xiàn)程對(duì)象。
2、就緒狀態(tài)(Runnable):線(xiàn)程對(duì)象創(chuàng)建后,其他線(xiàn)程調(diào)用了該對(duì)象的start()方法。該狀態(tài)的線(xiàn)程位于可運(yùn)行線(xiàn)程池中,變得可運(yùn)行,等待獲取CPU的使用權(quán)。
3、運(yùn)行狀態(tài)(Running):就緒狀態(tài)的線(xiàn)程獲取了CPU,執(zhí)行程序代碼。
4、阻塞狀態(tài)(Blocked):阻塞狀態(tài)是線(xiàn)程因?yàn)槟撤N原因放棄CPU使用權(quán),暫時(shí)停止運(yùn)行。直到線(xiàn)程進(jìn)入就緒狀態(tài),才有機(jī)會(huì)轉(zhuǎn)到運(yùn)行狀態(tài)。阻塞的情況分三種:
(一)、等待阻塞:運(yùn)行的線(xiàn)程執(zhí)行wait()方法,JVM會(huì)把該線(xiàn)程放入等待池中。
(二)、同步阻塞:運(yùn)行的線(xiàn)程在獲取對(duì)象的同步鎖時(shí),若該同步鎖被別的線(xiàn)程占用,則JVM會(huì)把該線(xiàn)程放入鎖池中。
(三)、其他阻塞:運(yùn)行的線(xiàn)程執(zhí)行sleep()或join()方法,或者發(fā)出了I/O請(qǐng)求時(shí),JVM會(huì)把該線(xiàn)程置為阻塞狀態(tài)。當(dāng)sleep()狀態(tài)超時(shí)、join()等待線(xiàn)程終止或者超時(shí)、或者I/O處理完畢時(shí),線(xiàn)程重新轉(zhuǎn)入就緒狀態(tài)。
5、死亡狀態(tài)(Dead):線(xiàn)程執(zhí)行完了或者因異常退出了run()方法,該線(xiàn)程結(jié)束生命周期。
七、線(xiàn)程的調(diào)度
1、調(diào)整線(xiàn)程優(yōu)先級(jí):Java線(xiàn)程有優(yōu)先級(jí),優(yōu)先級(jí)高的線(xiàn)程會(huì)獲得較多的運(yùn)行機(jī)會(huì)。
Java線(xiàn)程的優(yōu)先級(jí)用整數(shù)表示,取值范圍是1~10,Thread類(lèi)有以下三個(gè)靜態(tài)常量:
static int MAX_PRIORITY
線(xiàn)程可以具有的最高優(yōu)先級(jí),取值為10。
static int MIN_PRIORITY
線(xiàn)程可以具有的最低優(yōu)先級(jí),取值為1。
static int NORM_PRIORITY
分配給線(xiàn)程的默認(rèn)優(yōu)先級(jí),取值為5。
Thread類(lèi)的setPriority()和getPriority()方法分別用來(lái)設(shè)置和獲取線(xiàn)程的優(yōu)先級(jí)。
每個(gè)線(xiàn)程都有默認(rèn)的優(yōu)先級(jí)。主線(xiàn)程的默認(rèn)優(yōu)先級(jí)為T(mén)hread.NORM_PRIORITY。
線(xiàn)程的優(yōu)先級(jí)有繼承關(guān)系,比如A線(xiàn)程中創(chuàng)建了B線(xiàn)程,那么B將和A具有相同的優(yōu)先級(jí)。
JVM提供了10個(gè)線(xiàn)程優(yōu)先級(jí),但與常見(jiàn)的操作系統(tǒng)都不能很好的映射。如果希望程序能移植到各個(gè)操作系統(tǒng)中,應(yīng)該僅僅使用Thread類(lèi)有以下三個(gè)靜態(tài)常量作為優(yōu)先級(jí),這樣能保證同樣的優(yōu)先級(jí)采用了同樣的調(diào)度方式。
2、線(xiàn)程睡眠:Thread.sleep(long millis)方法,使線(xiàn)程轉(zhuǎn)到阻塞狀態(tài)。millis參數(shù)設(shè)定睡眠的時(shí)間,以毫秒為單位。當(dāng)睡眠結(jié)束后,就轉(zhuǎn)為就緒(Runnable)狀態(tài)。sleep()平臺(tái)移植性好。
3、線(xiàn)程等待:Object類(lèi)中的wait()方法,導(dǎo)致當(dāng)前的線(xiàn)程等待,直到其他線(xiàn)程調(diào)用此對(duì)象的 notify() 方法或 notifyAll() 喚醒方法。這個(gè)兩個(gè)喚醒方法也是Object類(lèi)中的方法,行為等價(jià)于調(diào)用 wait(0) 一樣。
4、線(xiàn)程讓步:Thread.yield() 方法,暫停當(dāng)前正在執(zhí)行的線(xiàn)程對(duì)象,把執(zhí)行機(jī)會(huì)讓給相同或者更高優(yōu)先級(jí)的線(xiàn)程。
5、線(xiàn)程加入:join()方法,等待其他線(xiàn)程終止。在當(dāng)前線(xiàn)程中調(diào)用另一個(gè)線(xiàn)程的join()方法,則當(dāng)前線(xiàn)程轉(zhuǎn)入阻塞狀態(tài),直到另一個(gè)進(jìn)程運(yùn)行結(jié)束,當(dāng)前線(xiàn)程再由阻塞轉(zhuǎn)為就緒狀態(tài)。
6、線(xiàn)程喚醒:Object類(lèi)中的notify()方法,喚醒在此對(duì)象監(jiān)視器上等待的單個(gè)線(xiàn)程。如果所有線(xiàn)程都在此對(duì)象上等待,則會(huì)選擇喚醒其中一個(gè)線(xiàn)程。選擇是任意性的,并在對(duì)實(shí)現(xiàn)做出決定時(shí)發(fā)生。線(xiàn)程通過(guò)調(diào)用其中一個(gè) wait 方法,在對(duì)象的監(jiān)視器上等待。直到當(dāng)前的線(xiàn)程放棄此對(duì)象上的鎖定,才能繼續(xù)執(zhí)行被喚醒的線(xiàn)程。被喚醒的線(xiàn)程將以常規(guī)方式與在該對(duì)象上主動(dòng)同步的其他所有線(xiàn)程進(jìn)行競(jìng)爭(zhēng);例如,喚醒的線(xiàn)程在作為鎖定此對(duì)象的下一個(gè)線(xiàn)程方面沒(méi)有可靠的特權(quán)或劣勢(shì)。類(lèi)似的方法還有一個(gè)notifyAll(),喚醒在此對(duì)象監(jiān)視器上等待的所有線(xiàn)程。
注意:Thread中suspend()和resume()兩個(gè)方法在JDK1.5中已經(jīng)廢除,不再介紹。因?yàn)橛兴梨i傾向。
7、常見(jiàn)線(xiàn)程名詞解釋
主線(xiàn)程:JVM調(diào)用程序mian()所產(chǎn)生的線(xiàn)程。
當(dāng)前線(xiàn)程:這個(gè)是容易混淆的概念。一般指通過(guò)Thread.currentThread()來(lái)獲取的進(jìn)程。
后臺(tái)線(xiàn)程:指為其他線(xiàn)程提供服務(wù)的線(xiàn)程,也稱(chēng)為守護(hù)線(xiàn)程。JVM的垃圾回收線(xiàn)程就是一個(gè)后臺(tái)線(xiàn)程。
前臺(tái)線(xiàn)程:是指接受后臺(tái)線(xiàn)程服務(wù)的線(xiàn)程,其實(shí)前臺(tái)后臺(tái)線(xiàn)程是聯(lián)系在一起,就像傀儡和幕后操縱者一樣的關(guān)系??苁乔芭_(tái)線(xiàn)程、幕后操縱者是后臺(tái)線(xiàn)程。由前臺(tái)線(xiàn)程創(chuàng)建的線(xiàn)程默認(rèn)也是前臺(tái)線(xiàn)程??梢酝ㄟ^(guò)isDaemon()和setDaemon()方法來(lái)判斷和設(shè)置一個(gè)線(xiàn)程是否為后臺(tái)線(xiàn)程。