在現(xiàn)在的Hotspot VM中,當(dāng)調(diào)用了Java線程對象的start方法就會創(chuàng)建一個操作系統(tǒng)線程,該操作系統(tǒng)線程就屬于可供CPU調(diào)度的線程了甥郑,此時一個Java線程對象就與一個操作系統(tǒng)線程一一對應(yīng)逃魄。當(dāng)然,這只是HotSpot VM中線程的實現(xiàn)方式澜搅,其他JVM可有不同的實現(xiàn)伍俘。
一般說來,通過Java創(chuàng)建一個可供CPU調(diào)度的線程有兩種方式:
1店展、創(chuàng)建一個java.lang.Thread對象养篓,并調(diào)用其start方法秃流;
2赂蕴、通過本地方法(如JNI)創(chuàng)建本地線程并加入的VM中。
線程的概述(Introduction)
線程是一個程序的多個執(zhí)行路徑舶胀,執(zhí)行調(diào)度的單位概说,依托于進程存在。 線程不僅可以共享進程的內(nèi)存嚣伐,而且還擁有一個屬于自己的內(nèi)存空間糖赔,這段內(nèi)存空間也叫做線程棧,是在建立線程時由系統(tǒng)分配的轩端,主要用來保存線程內(nèi)部所使用的數(shù)據(jù)放典,如線程執(zhí)行函數(shù)中所定義的變量。
注意:Java中的多線程是一種搶占機制而不是分時機制。搶占機制指的是有多個線程處于可運行狀態(tài)奋构,但是只允許一個線程在運行壳影,他們通過競爭的方式搶占CPU。
定義一個線程(Defining a Thread)有兩種方法
/*** 使用繼承java.lang.Thread類的方式創(chuàng)建一個線程*/
publicclassThreadTestextendsThread {/*** 重寫(Override)run()方法 JVM會自動調(diào)用該方法/
publicvoidrun() {System.out.println("I'm running!");}}
注意:重寫(override)run()方法在該線程的start()方法被調(diào)用后弥臼,JVM會自動調(diào)用run方法來執(zhí)行任務(wù)宴咧;但是重載(overload)run()方法,該方法和普通的成員方法一樣径缅,并不會因調(diào)用該線程的start()方法而被JVM自動運行掺栅。 例如:
publicclassThreadTestextendsThread {/*** 重寫(Override)run()方法 JVM會自動調(diào)用該方法/
@Override?publicvoidrun() {System.out.println("I'm running!");}
/*** 重載(Overload)run()方法 和普通的方法一樣,并不會在該線程的start()方法被調(diào)用后被JVM自動運行*/
publicvoidrun(inttimes) {System.out.println("I'm running!(Overload)");}}
不建議使用此方法定義線程纳猪,因為采用繼承Thread的方式定義線程后氧卧,你不能在繼承其他的類了,導(dǎo)致程序的可擴展性大大降低兆旬。
2) 實現(xiàn)java.lang.Runnable接口
/*** 通過實現(xiàn)Runnable接口創(chuàng)建一個線程*/
publicclassThreadTestimplementsRunnable {publicvoidrun() {System.out.println("I'm running!");}}
任何一個線程的執(zhí)行的前提都是必須有Thread class的實例存在假抄,并且通過調(diào)用run()方法啟動線程。
1)如果線程是繼承Thread類丽猬,則創(chuàng)建方式如下:
ThreadTest1 tt =newThreadTest1();tt.start();
2)如果是實現(xiàn)Runnable接口宿饱,則創(chuàng)建方式如下:
ThreadTest2 tt =newThreadTest2();Thread t =newThread(tt);t.start();
*********************************************線程狀態(tài)圖 ***********************************
每個類都有自己的優(yōu)先級赞警,一般property用1-10的整數(shù)表示,默認優(yōu)先級是5世剖,優(yōu)先級最高是10笤虫;優(yōu)先級高的線程并不一定比優(yōu)先級低的線程執(zhí)行的機會高,只是執(zhí)行的機率高酬凳;默認一個線程的優(yōu)先級和創(chuàng)建他的線程優(yōu)先級相同遭庶;
2)Thread.sleep()/sleep(long millis)
當(dāng)前線程睡眠/millis的時間(millis指定睡眠時間是其最小的不執(zhí)行時間,因為sleep(millis)休眠到達后台诗,無法保證會被JVM立即調(diào)度)赐俗;sleep()是一個靜態(tài)方法(static method) ,所以他不會停止其他的線程也處于休眠狀態(tài)阻逮;線程sleep()時不會失去擁有的對象鎖。 作用:保持對象鎖事哭,讓出CPU瓜富,調(diào)用目的是不讓當(dāng)前線程獨自霸占該進程所獲取的CPU資源,以留一定的時間給其他線程執(zhí)行的機會谤辜;
讓出CPU的使用權(quán),給其他線程執(zhí)行機會丑念、讓同等優(yōu)先權(quán)的線程運行(但并不保證當(dāng)前線程會被JVM再次調(diào)度、使該線程重新進入Running狀態(tài))渔彰,如果沒有同等優(yōu)先權(quán)的線程推正,那么yield()方法將不會起作用。
使用該方法的線程會在此調(diào)用線程的run方法執(zhí)行完畢后再往下繼續(xù)執(zhí)行舔稀。
讓一個線程等待另一個線程完成才繼續(xù)執(zhí)行,A 線程線程執(zhí)行體中調(diào)用B 線程 的join方法产园,則A 線程被阻塞夜郁,直到線程B 執(zhí)行完畢,A 才得以繼續(xù)執(zhí)行屎即。
當(dāng)一個線程執(zhí)行到wait()方法時事富,他就進入到一個和該對象相關(guān)的等待池(Waiting Pool)中,同時失去了對象的機鎖—暫時的雕擂,wait后還要返還對象鎖。當(dāng)前線程必須擁有當(dāng)前對象的鎖井赌,如果當(dāng)前線程不是此鎖的擁有者贵扰,會拋出IllegalMonitorStateException異常,所以wait()必須在synchronized block中調(diào)用。
喚醒在當(dāng)前對象等待池中等待的第一個線程/所有線程纹坐。notify()/notifyAll()也必須擁有相同對象鎖列肢,否則也會拋出IllegalMonitorStateException異常。
Synchronized Block/方法控制對類成員變量的訪問拴还;Java中的每一個對象都有唯一的一個內(nèi)置的鎖欧聘,每個Synchronized Block/方法只有持有調(diào)用該方法被鎖定對象的鎖才可以訪問,否則所屬線程阻塞怀骤;機鎖具有獨占性、一旦被一個Thread持有弓摘,其他的Thread就不能再擁有(不能訪問其他同步方法)痕届,方法一旦執(zhí)行,就獨占該鎖锤窑,直到從該方法返回時才將鎖釋放,此后被阻塞的線程方能獲得該鎖渊啰,重新進入可執(zhí)行狀態(tài)申屹。