--------------------ASP.Net+Android+IOS開發、.Net培訓、期待與您交流! --------------------
1. 線程間通信
1. 概述
線程間通信:就是不同的線程共享同一資源,然後對資源進行不同的操作行爲,說白了,在執行的線程運行代碼是不一樣的,但是代碼中還含有共享資源。
2. 舉例說明
例如:例如就是有一資源(Res 煤),有兩個線程分別執行的是賦值和取出(兩輛卡車,一個是運來,一個是運走)
/*共享的資源*/
public class Res {
private String name;
private String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public StringgetSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
/*輸入類*/
public class Input implements Runnable {
private Res r = null;// 定義了一個資源
public Input(Res r) {// 通過構造方法初始化
this.r = r;
}
public void run() {
int x = 0;
while (true) {
if (x == 0) {
this.r.setName("張三");
this.r.setSex("男");
} else {
this.r.setName("Joney");
this.r.setSex("female");
}
x = (x + 1) % 2;
}
}
}
/*輸出類*/
public class Output implements Runnable {
private Res re = null;
public Output(Res r) {//初始化資源
this.re = r;
}
public void run() {
while (true) {
System.out.println(this.re.getName() + ":" + this.re.getSex());
}
}
}
/*測試類*/
public class Text {
public static void main(String[] agrs) {
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
new Thread(in).start();
new Thread(out).start();
}
}
從結果中可以看出,結果和我們預測的結果不一樣,會出現這樣的結果:
Joney:男
張三:female
Joney:female
張三:男
張三:female
3. 優化通信
優化,剛纔的通信,就是加上同步鎖,在加鎖之前,一定要明確,必須是兩個或者是多個線程,鎖的對象必須是同一個鎖
/*輸入類*/
public class Input implements Runnable {
private Res r = null;// 定義了一個資源
public Input(Res r) {// 通過構造方法初始化
this.r = r;
}
public void run() {
int x = 0;
while (true) {
synchronized (r) {//加鎖,對象指定的是共享資源
if (x == 0) {
this.r.setName("張三");
this.r.setSex("男");
} else {
this.r.setName("Joney");
this.r.setSex("female");
}
}
x = (x + 1) % 2;
}
}
}
/*輸出類*/
public class Output implements Runnable {
private Res re = null;
public Output(Res r) {//初始化資源
this.re = r;
}
public void run() {
while (true) {
synchronized (re) {//加鎖,對象指定的是共享資源
System.out.println(this.re.getName() + ":" + this.re.getSex());
}
}
}
}
這樣結果就是相匹配了,這樣我們一定要靈活運用同步,並且記住同步需要的條件。
2. 等待喚醒機制
1. 概述和機制
利用wait(),notify()和notifyAll()三個方法。
wait()方法是線程放棄的執行資格,notify()喚醒監視器上等待的線程,默認的是從第一個開始。notifyAll()喚醒的是監視器(鎖)上所有等待的線程。爲什麼這三個函數都是在Object類中,因爲監視器(鎖)對象是任何對象,要用對象來是線程等待和喚醒,那麼就得放在Object類中,才能調用任何對象的方法。
在共享資源資源上,定義了一個可以標識資源狀態 的標識位,在監視器判斷標誌位,若不符合執行條件則是線程等待,用此時監視器對象來使線程等待,否則繼續執行,到最後改變標誌位,然後喚醒的等待的線程(是另外一個共享資源的線程),同樣另外一個線程(或者是多個)會執行同樣的操作。
只有同一個鎖上的被等待的線程,可以被同一個鎖上的notify()喚醒,不能對不同鎖上的線程進行喚醒,就是等待和喚醒必須是同一個鎖。
2. 舉例
/*共享的資源*/
public class Res {
private String name;
private String sex;
private boolean flag = false;//標誌位,用來表示資源的狀態
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
/*輸入類*/
public class Input implements Runnable {
private Res r = null;// 定義了一個資源
public Input(Res r) {// 通過構造方法初始化
this.r = r;
}
public void run() {
int x = 0;
while (true) {
synchronized (r) {// 加鎖,對象指定的是共享資源
if (r.isFlag())// flag=true表示此時裏面有資源,或者是資源沒有被讀取呢,不能繼續添加
try {
r.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (x == 0) {
this.r.setName("張三");
this.r.setSex("男");
} else {
this.r.setName("Joney");
this.r.setSex("female");
}
x = (x + 1) % 2;
r.setFlag(true);// 標識此時已經賦值了,不能在連續賦值了
r.notify();
}
}
}
}
/*輸出類*/
public class Output implements Runnable {
private Res r = null;
public Output(Res r) {// 初始化資源
this.r = r;
}
public void run() {
while (true) {
synchronized (r) {
if (!r.isFlag())// flag=false,標識此時沒有資源,或者資源已經被讀取了
try {
r.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.r.getName() + ":" + this.r.getSex());
r.setFlag(false);// 讀取資源後,然後把標誌爲false,標識此時已經被讀取出來了,不能在讀取了
r.notify();
}
}
}
}
/*測試類*/
public class Text {
public static void main(String[] agrs) {
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
new Thread(in).start();
new Thread(out).start();
}
}
結果:
張三:男
Joney:female
張三:男
Joney:female
張三:男
Joney:female
張三:男
Joney:female
張三:男
3.優化例子
在共享資源中使用同步方法老設置名稱和取值
/*共享的資源*/
public class Res {
private String name;
private String sex;
private boolean flag = false;// 標誌位,用來表示資源的狀態
/*同步函數賦值對象是 this*/
public synchronized void set(String name, String sex) {// 使用同步函數
if (flag)
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
this.name = name;//賦值
this.sex = sex;
this.flag = true;//改變標識位
this.notify();//喚醒等待線程
}
/*同步函數讀取 對象是this*/
public synchronized void show() {// 同步函數
if (!flag)
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.name + ":" + this.sex);//讀取
this.flag = false;//改變標識位
this.notify();//喚醒等待線程
}
}
/*輸入類*/
public class Input implements Runnable {
private Res r = null;// 定義了一個資源
public Input(Res r) {// 通過構造方法初始化
this.r = r;
}
public void run() {
int x = 0;
while (true) {
if (x == 0) {
r.set("張三", "男");
} else {
r.set("Joney", "female");
}
x = (x + 1) % 2;
}
}
}
/*輸出類*/
public class Output implements Runnable {
private Res r = null;
public Output(Res r) {// 初始化資源
this.r = r;
}
public void run() {
while (true) {
r.show();
}
}
}
/*測試類*/
public class Text {
public static void main(String[] agrs) {
Res r = new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
--------------------ASP.Net+Android+IOS開發、.Net培訓、期待與您交流! --------------------