1. 程式人生 > >《瘋狂Java講義(第4版)》-----第16章【多執行緒】(控制執行緒、執行緒同步)

《瘋狂Java講義(第4版)》-----第16章【多執行緒】(控制執行緒、執行緒同步)

控制執行緒

join執行緒

等那個執行緒做完後,當前執行緒再做!

import java.lang.Thread;

public class MyThread extends Thread{
	
	public MyThread(String name){
		super(name);
	}
	
	public void run(){
		for(int i = 0; i < 4; i++){
			System.out.println(getName()+" "+i);
		}
	}

	public static void main(String[] args)
throws Exception{ new MyThread("新執行緒").start(); for(int i = 0; i < 8; i++){ System.out.println(Thread.currentThread().getName()+" "+i); if(i == 4){ MyThread t = new MyThread("被join的執行緒"); t.start(); t.join(); } } } }

上面的程式中,i<4的時候,主執行緒main和“新執行緒”併發執行,i=4之後,主執行緒等待“被join的執行緒”執行完後,再執行。
在這裡插入圖片描述


一共有三個join方法如下:
在這裡插入圖片描述

後臺執行緒

有一種執行緒,在後臺執行,為其他執行緒提供服務,這種執行緒被稱為“後臺執行緒”,或“守護執行緒”,或“精靈執行緒”。JVM的垃圾回收執行緒就是後臺執行緒。

  1. 如果所有的前臺執行緒都死亡,JVM會通知後臺執行緒死亡。
  2. 呼叫Thread的setDaemon(true)方法可以把執行緒設定為後臺執行緒,注意必須是呼叫start方法之前設定。
  3. 主執行緒預設是前臺執行緒,預設是前臺執行緒的建立的子執行緒也預設是前臺執行緒,預設是後臺執行緒的建立的子執行緒預設也是後臺執行緒
import java.lang.Thread;
public class MyThread extends Thread{ public MyThread(String name){ super(name); } public void run(){ //這裡的i的範圍搞大點,為了讓前臺main執行緒執行完後,後臺執行緒就自動死亡 for(int i = 0; i < 1000; i++){ System.out.println(getName()+" "+i); } } public static void main(String[] args) throws Exception{ MyThread t = new MyThread("新執行緒"); t.setDaemon(true);//設定為後臺執行緒 t.start(); for(int i = 0; i < 8; i++){ System.out.println(Thread.currentThread().getName()+" "+i); } } }

在這裡插入圖片描述

執行緒睡眠:sleep

讓執行緒進入阻塞狀態,即使系統中沒有其他可執行的執行緒,處於sleep狀態的執行緒也不會執行。
在這裡插入圖片描述
下面程式執行,可以觀察到,每列印一次就暫停一秒!
在這裡插入圖片描述

在這裡插入圖片描述

改變執行緒優先順序

基於優先順序搶佔式排程策略情況下,自然優先順序高被排程到的概率大了!!!展示的程式就是設定優先順序有懸殊,可以看到優先順序高的執行緒被排程的次數比較多了。。。展示程式參看《瘋狂Java講義(第4版)》742~743頁。

每個執行緒預設的優先順序和建立他的父執行緒的優先順序相同。

改變優先順序和獲得優先順序的方法:
在這裡插入圖片描述
在這裡插入圖片描述

優先順序的值可以手動設定為1~10,怎麼知道這個範圍的呢?可以檢視這幾個值啊,如下下圖。但是要注意的是,最好使用這幾個靜態常量設定優先順序,因為這些優先順序依賴於作業系統的支援,手動寫上優先順序,可移植性弱。。
在這裡插入圖片描述在這裡插入圖片描述

執行緒同步

銀行取錢例子說明多執行緒(執行緒併發)具有安全問題,見《瘋狂Java講義(第4版)》743~745頁

同步程式碼塊、方法

用synchronized修飾程式碼塊、方法(不可修飾構造器、成員變數)。

obj是同步監視器,Java允許把任何物件(注意是物件)作為同步監視器,但根據同步監視器的目的—阻止兩個執行緒對同一個共享資源進行併發訪問,通常把有可能被併發訪問的共享資源(臨界區)作為同步監視器。任何時刻只能由一個執行緒可以獲得對同步監視器的鎖定,當同步程式碼執行完後,該執行緒就會釋放對該同步監視器的鎖定。

synchronized(obj){

}

同步鎖

常用的鎖是ReentrantLock(可重入鎖)。注意加鎖的是物件(注意是物件)。具體例項參看《瘋狂Java講義(第4版)》750~751頁

class X{
	private final ReentrantLock lock = new ReentrantLock();
	//...
	public void m(){
		//加鎖
		lock.lock();
		try{
			//需要保證執行緒安全的程式碼		
		}
		//用finally保證釋放鎖
		finally{
			lock.unlock();
		}
	}
}

使用Lock與使用同步方法是類似的,只是使用Lock時顯式使用Lock物件作為同步鎖,而使用同步方法時系統隱式使用當前物件作為同步監視器,,都是“加鎖–修改—釋放鎖”模式,同一時刻只有一個執行緒進入臨界區。

ReentrantLock鎖有可重入性,也就是說,一個執行緒可以對已加鎖的ReentrantLock鎖再次加鎖。

死鎖

當兩個執行緒相互等待對方釋放同步監視器時就會發生死鎖,Java虛擬機器沒有監測,也沒有采取措施來處理死鎖情況,所以多執行緒程式設計時應該採取措施避免死鎖出現。一旦出現死鎖,整個程式既不會發生任何異常,也不會給出任何提示,只是所有執行緒處於阻塞狀態,無法繼續。具體參看《瘋狂Java講義(第4版)》751~752頁示例程式碼。