多執行緒迴圈交替輸出1-100【extends Thread】
實踐目標:使用兩個執行緒列印 1-100. 執行緒1, 執行緒2 交替列印
其實就是多執行緒之間的執行緒通訊,使用wait、notify或者notifyAll。
如下的三個關鍵字使用的話,都得在同步程式碼塊或同步方法中。
① wait():一旦一個執行緒執行到wait(),就釋放當前的鎖。
② notify()喚醒wait的一個的執行緒;
③ notifyAll():喚醒所有執行緒;
示例程式碼如下:
class PrintNum extends Thread {
static int num = 1;
// 靜態成員變數,保證鎖的唯一
static Object obj = new Object();
public void run() {
while (true) {
//obj can instead of PrintNum.class
synchronized (obj) {
/*當前執行緒活動期間才能喚醒其他等待執行緒*/
obj.notify();
if (num <= 100) {
try {
Thread.currentThread().sleep(10 );
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":"
+ num);
num++;
} else {
break ;
}
try {
obj.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
public class TestCommunication {
public static void main(String[] args) {
Thread t1 = new PrintNum();
Thread t2 = new PrintNum();
t1.setName("thread1");
t2.setName("thread2");
t1.setPriority(Thread.MAX_PRIORITY);//10
t2.setPriority(Thread.MIN_PRIORITY);//1
t1.start();
t2.start();
}
}
呼叫start()方法,它 會新建一個執行緒然後執行run()方法中的程式碼。但是並不意味著多次呼叫t1.start()
會建立多個執行緒並執行run()方法!Thread執行了start之後不可以再次執行start!
那麼為什麼不直接呼叫run()方法呢?
如果直接呼叫run()方法,並不會建立新執行緒,方法中的程式碼會在當前呼叫者的執行緒中執行。
另外檢視Thread會發現,Thread實現了Runnable介面,實現了run方法並自定義了一系列方法。
故而使用實現Runnable並重寫run方法也可以:多執行緒實現Runnable介面
鎖的唯一性
這裡使用的鎖如下:
static Object obj = new Object();
在Main方法裡面我們建立了兩個PrintNum物件,那麼是不是有兩個Object obj = new Object();
?這樣豈不是不能保證執行緒安全,同步失敗?
這裡就要掌握類的載入及類中成員變數的載入。
例項化物件的執行順序
如果類還沒有被載入:
1、先執行父類的靜態程式碼塊和靜態變數初始化,並且靜態程式碼塊和靜態變數的執行順序只跟程式碼中出現的順序有關。
2、執行子類的靜態程式碼塊和靜態變數初始化。
3、執行父類的例項變數初始化(例如:int a;初始化就是0,引用型別就是null)
4、執行父類的建構函式
5、執行子類的例項變數初始化(例如:int a;初始化就是0,引用型別就是null)
6、執行子類的建構函式 。
如果類已經被載入:
則靜態程式碼塊和靜態變數就不用重複執行,再建立類物件時,只執行與例項相關的變數初始化和構造方法。
即 雖然建立了兩個PrintNum物件,但是靜態成員變數obj只有一個!這就保證了同步控制。
基於鎖的唯一性,那麼我們可以使用PrintNum.class來代理obj作為鎖進行同步控制。