《瘋狂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的垃圾回收執行緒就是後臺執行緒。
- 如果所有的前臺執行緒都死亡,JVM會通知後臺執行緒死亡。
- 呼叫Thread的setDaemon(true)方法可以把執行緒設定為後臺執行緒,注意必須是呼叫start方法之前設定。
- 主執行緒預設是前臺執行緒,預設是前臺執行緒的建立的子執行緒也預設是前臺執行緒,預設是後臺執行緒的建立的子執行緒預設也是後臺執行緒
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頁示例程式碼。