第九章 線程html
9.1 定義、實例化並啓動線程java
考試目標4.1 使用java.lang.Thread和java.lang.Runnable編寫代碼,定義、實例化並啓動新線程。編程
9.1.1 定義線程安全
擴展java.lang.Thread類,重寫run()方法。併發
class MyThread extends Thread{ public void run(){ System.out.println("Important job running in MyThread"); } }
實現java.lang.Runnable,實現run()方法。函數
class MyRunnable implements Runnable{ public void run(){ System.out.println("Important job running in MyRunnable"); } }
9.1.2 實例化線程spa
每一個執行線程都是做爲Thread類的一個實例開始的。線程
對於擴展Thread類而定義的線程實例化方式以下:設計
MyThread t = new MyThread();
對於實現Runnable接口而定義的線程實例化方式以下:code
MyRunnable r = new MyRunnable(); Thread t = new Thread(r);
t.start();
在調用start()方法後,發生:
啓動新的執行線程(具備新的調用棧)。
線程重新狀態轉到可運行狀態(runnable state)。
當線程得到執行機會時,會運行它的目標run()方法。
Thread.currentThread()用來獲取當前線程引用。
Thread.getName()用來得到線程的名字。
啓動並運行多個線程:
每一個線程都會啓動,並且每一個線程都將運行到結束。可是順序,優先沒有絕對保證。
當線程的目標run()方法結束時,該線程就完成了。死線程不能再次調用start()方法。
只要線程已經啓動過,它就永遠不能再次啓動。
線程調度器:
可運行線程編程運行中線程的順序是沒有保證的。
java.lang.Thread類中控制(影響)線程的方法:
public static void sleep(long millis) throws InterruptedException public static void yield() public final void join() throws InterruptedException public final void setPriority(int newPriority) //默認是5,值越大,優先越高 1-10
java.lang.Object類中控制(影響)線程的方法:
public final void wait() throws InterruptedException public final void notify() //喚醒單個線程 public final void notifyAll() //喚醒全部線程
9.2 線程狀態與轉變
考試目標4.2 識別線程可以位於哪些狀態,並肯定線程能從一種狀態轉變成另外一種狀態的方式。
9.2.1 線程狀態
新狀態--new
可運行狀態--runable
運行中狀態--running
等待/阻塞/睡眠狀態--waiting/blocked/sleeping
死狀態--dead
9.2.2 阻止線程執行
一個線程被踢出「運行中」狀態,而不是被送回「可運行」或「死」狀態。
即:睡眠,等待,由於須要對象的鎖而被阻塞。
9.2.3 睡眠
Thread的兩個靜態方法:
public static void sleep(long millis) throws InterruptedException //millis是毫秒
public static void sleep(long millis,int nanos) throws InterruptedException //nanos是納秒
9.2.4 線程優先級和yield()
調度器在優先級上是沒有保證的,主要是看它「喜歡。。仍是不喜歡」,真夠無奈的。
設置線程的優先級
t.setPriority(int i);
yield()方法
Thread的靜態方法yield(),讓當前的「運行中」線程回到「可運行」狀態,讓步給具備相同優先級的其餘「可運行」線程,但這是沒有任何保證的。
join()方法
Thread的非靜態方法join()讓當前線程加入到引用的線程尾部,這意味着調用方法的線程完成(死狀態)以前,主線程不會變爲可運行的。
public final void join() //要一直等待該線程結束 throws InterruptedException
public final void join(long millis) //millis爲0表示要一直等下去 throws InterruptedException
public final void join(long millis, int nanos) throws InterruptedException
9.3 同步代碼
考試目標4.3 給定一個場景,編寫代碼,恰當地使用對象鎖定來保護靜態變量或實例變量,使它們不出險併發訪問問題。
9.3.1 同步和鎖 synchronized
-
只能同步方法(或代碼塊),不能同步變量或類
-
每一個對象只有一個鎖。換句話說同一時間只有一個線程可使用該對象的同步方法或代碼塊。
-
沒必要同步類中的所有方法。類能夠同時具備同步方法和非同步方法。
-
一旦一個線程得到了對象的鎖,就沒有任何其餘線程能夠進入(該對象的)類中的任務同步方法。
-
若是類同事具備同步和非同步方法,則多個線程仍然可以訪問該類的非同步方法。
-
若是線程進入睡眠,則它會保持已有的任何鎖,不會釋放他們。
-
線程能夠得到多個鎖,即線程能夠同時得到多個對象的鎖。
靜態方法可否同步
能夠,靜態代碼塊也能夠。例如:
public static synchronized int getCount(){ return count; } public static int getCount(){ //注意synchronized關鍵字後面括號裏的內容:即要鎖的類或對象 synchronized(MyClass.class){ return count; } }
若是線程不能得到鎖會怎麼樣?
會阻塞,等鎖釋放。
什麼時候須要同步?
書雲:對於複雜的狀況「若是你不想它的話,你的生命就會更長、更愉快。真的如此,咱們沒有撒謊。」
線程安全類
StringBuffer等
9.3.2 線程死鎖
-
當代碼等待從對象刪除鎖而迫使線程執行暫停時,死鎖就發生了。
-
當被鎖的對象試圖訪問另外一個被鎖的對象,而該對象又要訪問第一個被鎖的對象時,就會發生死鎖現象。換句話說,兩個線程都在等待對方釋放鎖,所以,這些鎖永遠不會釋放!
-
死鎖是一種糟糕的設計,不該當讓它發生。
9.4 線程交互
考試目標4.4 給定一個場景,編寫代碼,恰當地使用wait()、notify()和notifyAll()方法。
必須在同步方法內調用wait()、notify()和notifyAll()方法!線程不能調用對象上的等待或通知方法,除非它擁有該對象的鎖。
9.4.1 當多個線程等待時使用notifyAll()
在循環中使用wait()