多執行緒的執行緒間通訊
阿新 • • 發佈:2019-01-07
一、兩個執行緒間的通訊
- 1.什麼時候需要通訊
- 多個執行緒併發執行時, 在預設情況下CPU是隨機切換執行緒的
- 如果我們希望他們有規律的執行, 就可以使用通訊, 例如每個執行緒執行一次列印
- 2.怎麼通訊
- 如果希望執行緒等待, 就呼叫wait()
- 如果希望喚醒等待的執行緒, 就呼叫notify();
- 這兩個方法必須在同步程式碼中執行, 並且使用同步鎖物件來呼叫
public class Demo1_Notify {
/**
* @param args
* 等待喚醒機制
*/
public static void main(String[] args) {
final Printer p = new Printer();
new Thread() {
public void run() {
while(true) {
try {
p.print1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
while(true) {
try {
p.print2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
//等待喚醒機制
class Printer {
private int flag = 1;
public void print1() throws InterruptedException {
synchronized(this) {
if(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.notify(); //隨機喚醒單個等待的執行緒
}
}
public void print2() throws InterruptedException {
synchronized(this) {
if(flag != 2) {
this.wait();
}
System.out.print("傳");
System.out.print("智");
System.out.print("播");
System.out.print("客");
System.out.print("\r\n");
flag = 1;
this.notify();
}
}
}
二、三個或三個以上執行緒通訊
- 如果多個執行緒之間通訊, 需要使用notifyAll()通知所有執行緒, 用while來反覆判斷條件
public class Demo2_NotifyAll {
/**
* @param args
*/
public static void main(String[] args) {
final Printer2 p = new Printer2();
new Thread() {
public void run() {
while(true) {
try {
p.print1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
while(true) {
try {
p.print2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
while(true) {
try {
p.print3();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
/*1,在同步程式碼塊中,用哪個物件鎖,就用哪個物件呼叫wait方法
* 2,為什麼wait方法和notify方法定義在Object這類中?
* 因為鎖物件可以是任意物件,Object是所有的類的基類,所以wait方法和notify方法需要定義在Object這個類中
* 3,sleep方法和wait方法的區別?
* a,sleep方法必須傳入引數,引數就是時間,時間到了自動醒來
* wait方法可以傳入引數也可以不傳入引數,傳入引數就是在引數的時間結束後等待,不傳入引數就是直接等待
* b,sleep方法在同步函式或同步程式碼塊中,不釋放鎖,睡著了也抱著鎖睡
* wait方法在同步函式或者同步程式碼塊中,釋放鎖
*/
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.notify(); //隨機喚醒單個等待的執行緒
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.notify();
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.notify();
this.notifyAll();
}
}
}
三、互斥鎖
- 1.同步
- 使用ReentrantLock類的lock()和unlock()方法進行同步
- 2.通訊
- 使用ReentrantLock類的newCondition()方法可以獲取Condition物件
- 需要等待的時候使用Condition的await()方法, 喚醒的時候用signal()方法
- 不同的執行緒使用不同的Condition, 這樣就能區分喚醒的時候找哪個執行緒了
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Demo3_ReentrantLock {
/**
* @param args
*/
public static void main(String[] args) {
final Printer3 p = new Printer3();
new Thread() {
public void run() {
while(true) {
try {
p.print1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
while(true) {
try {
p.print2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
while(true) {
try {
p.print3();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
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;
//this.notify(); //隨機喚醒單個等待的執行緒
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;
//this.notify();
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();
}
}