九、線程 start()對我們唯一的保證:每個線程都將運行,每個線程都將運行至結束 一旦start(),就永遠不能重新start()哪怕是它已經死去,否則導致運行時異常 需要熟悉的方法: 靜態yield(),大多數情況下使當前線程轉入可運行狀態,本意是使得具有相同優先級的其他線程獲得運行機會,這同樣無法得到保證 靜態sleep(),轉入可運行狀態休眠一段時間,不放任何鎖;用法Thread.sleep(long),必須包圍在try-catch中 join(),保證在調用join()的線程結束前,當前進程停止運行,也即當前執行線程內join()之后的語句保證在指定線程run()完之后運行 靜態Thread.currentThread(),返回對當前執行線程的引用 isAlive(),已經啟動,未結束run() start()的調用需要Thread的實例,不可以是runnable 使一個實現runnable接口的類的實例啟動,可以做一個Thread,也可以直接調用run方法 Runnable只有一個run方法,public void run();若沒有重寫這樣一個方法,將在聲明實現接口的地方報錯:未能實現該接口 線程的啟動應調用start(),若直接調用run()則表示從當前正在運行的類調用一個普通的方法,run()進入當前調用棧,而不會啟動新的線程;重載的run()只能作為一般方法手動調用 語法上,將線程傳遞給線程的構造器是合法的 對線程進行setPriority()是沒有保證的。默認的優先級是5 保護數據的操作:變量標識為private,同步訪問這些變量的方法 在java中,每一個對象都有一個內置的鎖,鎖表現出作用的唯一情況是:對象具有同步方法代碼。當使用一個實例調用非靜態同步方法時,就會索取該實例的鎖 對一個實例,某一時刻只能有一個線程鎖定它;拿到鎖的線程可以訪問該實例的任意同步方法/塊,其他線程可以任意訪問非同步方法 為了減少同步對并發的影響,可以只將方法的一部分代碼放入同步塊中實現同步——同步塊應當指定這部分代碼執行時要鎖定的對象是誰:當指定this時可以寫成一個普通的同步方法的形式;對于靜態方法,在外圍聲明為synchronized或在block中使用LockTargetClass.class的形式指示鎖定對象,因為靜態成員只有一份拷貝,要將整個類所有靜態成員聲明為同步 當對象的鎖被取走之后,其他試圖訪問同步方法的線程被阻塞在鎖上(進入阻塞狀態)。他們在某個特定的池等待對象的鎖被釋放,返回可運行狀態 關于線程在同步上的互相阻塞: 引用同一實例訪問非靜態同步方法的線程彼此阻塞 訪問同一類中靜態方法的線程彼此阻塞——他們都在索取同一個Class實例的鎖 訪問同一類靜態與非靜態同步方法的兩個線程不會彼此阻塞——一個在鎖定this,一個在鎖定class 同步不同對象的兩個方法不會彼此阻塞 保持鎖的方法:join(),sleep(),yield(),notify() 放棄鎖的方法:wait() Access to static fields should be done from static synchronized methods. Access to non-static fields should be done from non-static synchronized methods 線程安全類:不要把同步放在較高的級別上 兩個線程互相等待對方拿到的鎖——死鎖 Wait()、notify()等方法是Object類的方法,它們只能放入同步塊,這就保證了在獲得同步對象的鎖之前,不能在對象上調用這些方法 每一個對象都可以有一個表,放置等待它信號的線程;一旦線程調用該對象的wait(),他們就進入這個列表之中,在目標對象調用notify()之前,這些線程什么都不做——也就是說,看到wait()但找不到notify(),則wait()之后的東西永遠不會發生(假如可以正確編譯的話)在wait時,線將程釋放鎖 a waiting thread will not return to runnable when the lock is released, unless a notification occurs 考慮:有時notify()可能會先于wait()而發生調用。這樣notify()不會再發生,而后開始的wait()等不到結果。所以在等待之前,應當檢查notify()是否已經發生。 阻止線程運行總結: 睡眠:睡眠指定的時間,醒來時是可運行狀態,也即不保證醒來就是運行狀態;sleep()是一個靜態方法,因此在不能有一種做法可以讓一個線程使另一個線程睡眠——當前運行線程的代碼看到sleep()時,即刻睡去 等待: 因為需要一個對象的鎖定而被阻塞