JAVA篇:Java 多執行緒 (一) 執行緒控制
1、執行緒控制
關鍵字:wait/notify/notifyAll、join、sleep、interrupt
執行緒控制討論執行緒在呼叫了start()到執行完成中間階段的行為,包含
-
執行緒阻塞和喚醒、超時等待
-
執行緒中斷機制
1.1 執行緒阻塞和喚醒、超時等待
主要討論join(),wait()、notify()和notifyAll(),以及yield()和sleep()。以及期間cpu資源及鎖資源的情況,這裡的鎖僅僅考慮synchronized(Object)物件鎖。
1.1.1 join() 方法
join()是執行緒的例項方法,有兩種形式,thread.join()和tread.join(long timeout)。join方法會阻塞當前執行緒,等待指定執行緒執行完畢後才會被喚醒,或者如果設定了超時,等到超時後當前執行緒也會被喚醒
join()方法使得當前執行緒休眠,釋放cpu資源,但是並不會釋放鎖。
有說法說“其底層實現是wait()方法,會釋放鎖。”然後我寫了一個測試程式碼,形成了死鎖。wait()方法釋放鎖的相關討論在後文,在這裡先討論join()方法執行過程中的情況。
join()的字首是執行緒例項。如果要描述得清楚些則需要做一些假設。譬如說由執行緒A和t1,t1處於執行狀態,在當前執行緒A呼叫t1.join()。那麼執行緒A會無限阻塞,直到t1執行結束。
那麼A在呼叫了t1.join()後等待t1的期間是否會釋放資源呢?我感覺釋放資源這個要往細了說,釋放什麼資源?釋放cpu資源,釋放cpu資源和全部鎖資源,釋放cpu資源和指定鎖資源。
有說法join()方法呼叫之後會釋放鎖,總不可能是釋放t1持有的鎖吧,所以只能理解為釋放當前執行緒A持有的鎖。但是join方法不同,它是由t1呼叫的,也無法預見t1會需要什麼鎖資源,那麼A呼叫t1.join()只可能是任意釋放一個鎖,或者說更加靠譜地釋放全部鎖。
我按照這個思路寫了測試程式碼,但是主執行緒呼叫了join()並未釋放任何鎖,然後兩個子執行緒都無法獲得鎖,造成了死鎖。
為了防止死鎖,我將join()方法設定了超時,使得程式碼可以執行,最後的程式碼和結果如下:
/* 測試join() */ public void test2(){ /* 模擬共享資源*/ Object res = new Object(); Object res2 = new Object(); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); /*建立子執行緒1,共享資源res*/ Runnable r1 = new Runnable() { @Override public void run() { try { Thread.sleep(3000); System.out.println(df.format(new Date())+" part11:子執行緒1休眠結束,嘗試請求res鎖"); synchronized (res){ System.out.println(df.format(new Date())+" part12:子執行緒1獲得res鎖,後進入休眠"); Thread.sleep(3000); System.out.println(df.format(new Date())+" part13:子執行緒1結束休眠,並釋放res鎖"); } } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread t1 = new Thread(r1); t1.start(); /*建立子執行緒2,共享資源res2*/ Runnable r2 = new Runnable() { @Override public void run() { try { Thread.sleep(3000); System.out.println(df.format(new Date())+" part21:子執行緒2休眠結束,嘗試請求res2鎖"); synchronized (res2){ System.out.println(df.format(new Date())+" part22:子執行緒2獲得res2鎖,後進入休眠"); Thread.sleep(3000); System.out.println(df.format(new Date())+" part23:子執行緒2結束休眠,並釋放res2鎖"); } } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread t2 = new Thread(r2); t2.start(); /* 主執行緒持有鎖,然後呼叫join */ synchronized (res2){ System.out.println(df.format(new Date())+" part01:主執行緒持有res2鎖"); synchronized (res){ System.out.println(df.format(new Date())+" part02:主執行緒持有res鎖"); System.out.println(df.format(new Date())+" part03:主執行緒呼叫t1,t2的join()"); try { t1.join(10000);//有等待時限的join t2.join(10000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(df.format(new Date())+" part04:主執行緒退出join,不再等待,釋放鎖res"); } System.out.println(df.format(new Date())+" part05:主執行緒釋放鎖res2"); } System.out.println(df.format(new Date())+" 測試結束。"); }