天天看點

Java多線程suspend、sleep的控制鎖的釋放的差別

         因為馬上畢業了,最近一直在複習Java的基礎知識,多線程當然是重點了,今天上午一直在看線程的生命階段,其中有過時的方法suspend用來挂起一個線程。而關于該方法為何被抛棄了,看了開發文檔中是這麼描述的:【方法已經遭到反對,因為它具有固有的死鎖傾向。如果目标線程挂起時在保護關鍵系統資源的螢幕上保持有鎖,則在目标線程重新開始以前任何線程都不能通路該資源。如果重新開始目标線程的線程想在調用

resume

之前鎖定該螢幕,則會發生死鎖。】

看完上面的官方文檔,光看文字把頭都看暈了,感覺sleep和suspend對挂起線程的操作感覺又模糊了不少,尤其是對鎖的控制上面。

是以就模拟了兩個線程,代碼如下:

package a.b;
public class Test {
	private static final int INDEX = 10;
	public static void main(String[] args) {
		try {
			// 定義線程
			Thread tchild = new Thread(new Runnable() {
				public void run() {
					try {
						int a = 0;
						for (long i = 0; i < 1000000; i++) {
						while (a < INDEX)/** 如果在執行這句的時候調用了tchild.suspend(),那就不會導緻死鎖 */
							a++;
						}
						System.out.println(i);
						/**
						* 如果正好在執行上面這句的時候調用了tchild.suspend(),
						* 由于println()方法裡面保持有鎖,
						* 是以,在本線程挂起的時候其他的線程就無法使用println方法中所持有的鎖,
						* 這就導緻suspend方法容易導緻死鎖的原因。
						*/
						a = 0;/** 如果在執行這句的時候調用了tchid.suspend(),那就不會導緻死鎖 *//
					}
				} catch (Throwable e) {e.printStackTrace();}
			}//run
		});
		tchild.start();/**開啟子線程*/
		Thread.sleep(2000);/**讓主線程停止2s,子線程繼續運作2s*/
		tchild.suspend();/**讓子線程挂起*/
		
		/**讓子線程挂起的情況下再執行主線程中的列印操作*/
		for (long i = 0; i < 1000000; i++) {
			System.out.println("主線程"+i);
		}
		tchild.resume();//激活被挂起的線程 } catch (Throwable ex) { ex.printStackTrace(); } }
}
           

實驗結果是:

1)當常量INDEX=10的時候,輸出的結果大部分情況下是:運作到:    

....

子線程152463

子線程152464

子線程152465

此時,列印了子線程的資訊過了兩秒後,子線程被挂起,但是主線程中的内容卻沒有繼續執行,這說明主線程已經被阻塞了;

原因是:子線程在運作到System.out.println(i);這一句的時候被suspend()方法挂起了,而由于該句調用的時候使用了鎖,

即out中的常量public final static PrintStream out = null;此處的out對象就是方法println中使用的鎖對象;是以在被suspend挂起後子線程任然持有鎖-out常量,是以當主線程運作for循環列印資訊的時候System.out.println("主線程"+i);,主線程根本拿不到鎖,是以造成線程死鎖;

2)當常量INDEX=100000000的時候,輸出結果大部分情況下是,運作到:

.....

主線程999998

主線程999999

子線程2605

子線程2606

...

子線程999998

子線程999999

,同樣的道理因為子線程并不是在運作System.out.println(i);這一句的時候被suspend()方法挂起的,是以就不會持有列印語句中的鎖-out常量,是以不會産生死鎖現象。

綜合上面的分析:

調用suspend方法來挂起一個線程A的時候,如果這好在執行一端具有同步鎖的代碼塊的時候被挂起,那這個同步鎖是不會被釋放的,那麼當線程B執行時,如果内部代碼需要使用到該鎖,而此時的鎖是被線程B中代碼塊持有的,此時就會導緻線程死鎖。這就是為什麼suspend不安全的地方,因為它無法控制線程内部代碼塊持有的鎖的釋放。

關于sleep調用後不會釋放鎖是指的,當線程A的同步代碼塊中調用了sleep的使用,該代碼塊持有的鎖不會被釋放,那麼線程B,C,..去執行具有相同鎖對象的代碼塊的時候就會被阻塞。