一、兩個線程間的通信
1.什麼時候需要通信
多個線程併發執行時,在默認情況下CPU是隨機切換線程的,如果我們希望他們有規律的執行, 就可以使用通信, 例如每個線程執行一次打印
2.如何通信
如果希望線程等待,就調用wait()
如果希望喚醒等待的線程,就調用notify()
這兩個方法必須在同步代碼中執行, 並且使用同步鎖對象來調用
3.代碼
class Printer {
private int flag = 1;
public void print1() throws InterruptedException {
synchronized(this) {
if(flag != 1) {
this.wait(); //當前線程等待
}
System.out.print("1");
System.out.print("2");
System.out.print("3");
System.out.print("4");
System.out.print("5");
System.out.print("\r\n");
flag = 2;
this.notify(); //隨機喚醒單個等待的線程
}
}
public void print2() throws InterruptedException {
synchronized(this) {
if(flag != 2) {
this.wait();
}
System.out.print("a");
System.out.print("b");
System.out.print("c");
System.out.print("d");
System.out.print("\r\n");
flag = 1;
this.notify();
}
}
}
二、三個及以上線程間的通信
1.如何通信
notify()方法是隨機喚醒一個線程,notifyAll()方法是喚醒所有線程
JDK5之前無法喚醒指定的一個線程,如果多個線程之間通信,需要使用notifyAll()通知所有線程,用while來反覆判斷條件
2.代碼
class Printer2 {
private int flag = 1;
public void print1() throws InterruptedException {
synchronized(this) {
while(flag != 1) {
this.wait();
}
System.out.print("黑");
System.out.print("馬");
System.out.print("程");
System.out.print("序");
System.out.print("員");
System.out.print("\r\n");
flag = 2;
this.notifyAll();
}
}
public void print2() throws InterruptedException {
synchronized(this) {
while(flag != 2) {
this.wait(); //線程2在此等待
}
System.out.print("傳");
System.out.print("智");
System.out.print("播");
System.out.print("客");
System.out.print("\r\n");
flag = 3;
this.notifyAll();
}
}
public void print3() throws InterruptedException {
synchronized(this) {
while(flag != 3) {
this.wait(); //線程3在此等待,if語句是在哪裏等待,就在哪裏起來
//while循環是循環判斷,每次都會判斷標記
}
System.out.print("i");
System.out.print("t");
System.out.print("h");
System.out.print("e");
System.out.print("i");
System.out.print("m");
System.out.print("a");
System.out.print("\r\n");
flag = 1;
this.notifyAll();
}
}
}
注:
- 1.在同步代碼塊中,用哪個對象鎖,就用哪個對象調用wait方法
- 2.爲什麼wait方法和notify方法定義在Object這類中?
因爲鎖對象可以是任意對象,Object是所有的類的基類,所以wait方法和notify方法需要定義在Object這個類中
- 3.sleep方法和wait方法的區別?
sleep方法必須傳入參數,參數就是時間,時間到了自動醒來
wait方法可以傳入參數也可以不傳入參數,傳入參數就是在參數的時間結束後等待,不傳入參數就是直接等待
sleep方法在同步函數或同步代碼塊中,不釋放鎖,睡着了也抱着鎖睡
wait方法在同步函數或者同步代碼塊中,釋放鎖
三、JDK1.5的新特性互斥鎖
1.同步
使用ReentrantLock類的lock()和unlock()方法進行同步
2.通信
使用ReentrantLock類的newCondition()方法可以獲取Condition對象
需要等待的時候使用Condition的await()方法,喚醒的時候用signal()方法
不同的線程使用不同的Condition,這樣就能區分喚醒的時候找哪個線程了
3.代碼
class Printer3 {
private ReentrantLock r = new ReentrantLock();
private Condition c1 = r.newCondition();
private Condition c2 = r.newCondition();
private Condition c3 = r.newCondition();
private int flag = 1;
public void print1() throws InterruptedException {
r.lock(); //獲取鎖
if(flag != 1) {
c1.await();
}
System.out.print("黑");
System.out.print("馬");
System.out.print("程");
System.out.print("序");
System.out.print("員");
System.out.print("\r\n");
flag = 2;
c2.signal();
r.unlock(); //釋放鎖
}
public void print2() throws InterruptedException {
r.lock();
if(flag != 2) {
c2.await();
}
System.out.print("傳");
System.out.print("智");
System.out.print("播");
System.out.print("客");
System.out.print("\r\n");
flag = 3;
c3.signal();
r.unlock();
}
public void print3() throws InterruptedException {
r.lock();
if(flag != 3) {
c3.await();
}
System.out.print("i");
System.out.print("t");
System.out.print("h");
System.out.print("e");
System.out.print("i");
System.out.print("m");
System.out.print("a");
System.out.print("\r\n");
flag = 1;
c1.signal();
r.unlock();
}
}