Java創(chuàng)建一個線程,最簡單的方法就是從Thread類繼承。這個類包含了創(chuàng)建和運行線程所需的一切東西。Tread最重要的方法是run()。但為了使用run(),必須對其進行重載或者覆蓋,使其能充分按自己的吩咐形式。因此,run()屬于那些會與程序中的其他線程“并發(fā)”或“同時”執(zhí)行代碼。
//simpleThread.java
public class SimpleThread extends Thread {
......
public void run(){
// do something ......
}
}
Thread包含一個特殊的方法 start(),它的作用是對線程進行特殊的初始化,然后調用 run() 。所以整個步驟包括:調用構建器來構建對象,然后用 start() 配置線程,再調用 run() 。如果不調用 start(),線程便永遠不會啟動。
Java操作共享資源時,為了保證共享資源信息數據一致性,在調用共享資源的方法使用,需要采用“同步”方式操作,即調用"synchronized"方法。調用任何synchronized方法時,對象就會被鎖定,不可再調用那個對象的其它任何synchronized方法,除非第一個方法完成了自己的工作,并解除鎖定。下面列出簡單的synchronized的方法:
synchronized void f(){
// do something ......
}
synchronized void g(){
// do something ......
}
重要規(guī)則:對于訪問某個關鍵共享資源的所有方法,都必須把它們設為synchronized,否則就不能正常地工作。
一個線程的四個狀態(tài):
- 新(New):線程對象已經創(chuàng)建,但尚未啟動,所以不可運行。
- 可運行(Runnable):意味著一旦時機分片機制有空閑的CPU周期提供給一個線程,那個線程便立即開始運行。因此,線程可能在、也可能不在運行當中,但一旦條件許可,沒有什么能阻止他的運行——它既沒有“死”掉,也未被“堵塞”。
- 死(Dead):從自己的run()方法中返回后,一個線程便已“死”掉。亦可調用stop()令其死掉,但會產生一個違例——屬于Error的一個子類(也就是說,我們通常不捕獲它)。記住一個違例的“擲”出應當是一個特殊事件,而不是正常程序運行的一部分。所以不建議你使用stop()(在Java 1.2 則是堅決反對)。另外還有一個destroy()方法(它永遠不會實現),應該盡可能地避免調用它,因為它非常武斷,根本不會解除對象的鎖定。
- 堵塞(Blocked):線程可以運行,但有某種東西阻礙了它。若線程處于堵塞狀態(tài),調度機制可以簡單地跳過它,不給它分配任何CPU 時間。除非線程再次進入“可運行”狀態(tài),否則不會采取任何操作。
線程為何會堵塞?
堵塞狀態(tài)是前述四種狀態(tài)中最有趣的,值得我們作進一步的探討。線程被堵塞可能是由下述五方面的原因造成的:
1.調用sleep(毫秒數),使線程進入“睡眠”狀態(tài)。在規(guī)定的時間內,這個線程是不會運行的。
2.用suspend()暫停了線程的執(zhí)行。除非線程收到resume()消息,否則不會返回“可運行”狀態(tài)。
3.用wait()暫停了線程的執(zhí)行。除非線程收到nofify()或者notifyAll()消息,否則不會變成“可運行”(是的,這看起來同原因2 非常相象,但有一個明顯的區(qū)別是我們馬上要揭示的)。
4.線程正在等候一些IO(輸入輸出)操作完成。
5.線程試圖調用另一個對象的“同步”方法,但那個對象處于鎖定狀態(tài),暫時無法使用。
亦可調用yield()(Thread 類的一個方法)自動放棄CPU,以便其他線程能夠運行。
死鎖
由于線程可能進入堵塞狀態(tài),而且由于對象可能擁有“同步”方法——除非同步鎖定被解除,否則線程不能訪問那個對象——所以一個線程完全可能等候另一個對象,而另一個對象又在等候下一個對象,以此類推。這個“等候”鏈最可怕的情形就是進入封閉狀態(tài)——最后那個對象等候的是第一個對象!此時,所有線程都會陷入無休止的相互等待狀態(tài),大家都動彈不得。我們將這種情況稱為“死鎖”。
為減少出現死鎖的可能,Java 1.2 作出的一項貢獻是“反對”使用Thread 的stop(),suspend(),resume() 以及destroy()方法。
suspend()和resume() 方法天生容易發(fā)生死鎖。調用suspend()的時候,目標線程會停下來,但卻仍然持有在這之前獲得的鎖定。此時,其他任何線程都不能訪問鎖定的資源,除非被“掛起”的線程恢復運行。
推薦:使用wait()命其進入等待狀態(tài)。若標志指出線程應當恢復,則用一個
notify()重新啟動線程。
線程安全性:當多個線程訪問一個類時,如果不用考慮這些線程在運行時環(huán)境下的調度和交替執(zhí)行,并且不需要額外的同步及在調用方代碼不必做其他的協(xié)調,這個類的行為仍然是正確的,那么稱這個類是線程安全的。對于線程安全類的實例進行順序或并發(fā)的一系列操作,都不會導致實例處于無效狀態(tài)。
無狀態(tài)對象永遠是現場安全的。
原子變量
線程安全性定義要求無論是多線程中的時序或交替操作,都要保證不破壞那些不變約束。當一個不變約束涉及多個變量時,變量間不是彼此獨立的:某個變量的值會制約其他幾個變量的值。因此,更新一個變量的時候,要在同一原子操作中更新其他幾個。
為了保護狀態(tài)的一致性,要在單一的原子操作中更新相互關聯的狀態(tài)變量。
加鎖