JAVA學習第二十六課(多線程(五))- 多線程間的通訊問題,java多線程
一、線程間的通訊
執行個體代碼:
需求是:輸入一個姓名和性別後,就輸出一個姓名和性別
class Resource{String name;String sex ;}class Input implements Runnable{Resource r;Input(Resource r){this.r = r;}public void run(){int x = 0;while(true){synchronized (r) {if(x==0){r.name = "BLF";r.sex = "male";}else {r.name = "妮妮妮妮";r.sex = "female";}x = (++x)%2;}}}}class Output implements Runnable{Resource r;public Output(Resource r) {// TODO Auto-generated constructor stubthis.r = r;}public void run() {while(true){synchronized (r) //輸入和輸出都是應用同一個鎖{System.out.println(r.name+"..."+r.sex);}}}}public class Main {public static void main(String[] args){Resource r = new Resource();//共用同一資源Input in = new Input(r);Output out = new Output(r); Thread t1 = new Thread(in);Thread t2 = new Thread(out);t1.start();t2.start();}}
上述代碼雖然解決了,多執行緒同一資源問題,但是出現了一個問題就是,列印很多一個人名和性別後再列印另一個,無法實現交替輸出,原因:輸入線程擷取執行權後,不會執行一次,輸入姓名和性別,輸入線程還擁有執行權,一直賦值,等到切換到輸出執行權後,輸出最後輸入的姓名和性別,但是輸出線程也不會只輸出一次,一直輸出,所以出現列印很多同一姓名和性別的問題
二、線程的等待/喚醒機制:
用一個boolean值判斷是否有資料,有就不放,沒有就放
輸入線程執行時判斷是否有資料,有,就釋放執行權,再釋放執行資格,進入凍結狀態,輸出線程執行,輸出後boolean值置為false
Input: if(flag)//有值 wait();//當前凍結,切換輸出線程 flag = true; notify();Output: if(!flag) wait(); flag = false;//flag置為假,輸出完畢 notify();//喚醒輸入線程
等待/喚醒機制:
方法:
1.wait();//使線程進入凍結狀態,CPU釋放執行權和執行資格,被wait()的線程會被儲存到線程池中2.notify();//喚醒線程池中的任意的一個線程3.notifyAll();//喚醒所有線程,使之處於運行狀態或臨時阻塞狀態,總之,使其具備執行資格
這些方法必須定義在同步中,這些方法是用於操作線程狀態的方法,所以必須明確在哪個鎖上的線程
(wait()A鎖的線程,只能用A鎖的notify喚醒A鎖的線程。)
瞭解:而wait()等方法是定義在Object類中的,這些方法是監視器的方法,而監視器呢,就可以理解為鎖,控制哪個鎖所屬下的線程,而鎖又可以是任意的,而所有的類都是繼承於Object類的,所以wait()等類是定義在Object裡
下述代碼就解決了上述代碼的問題,實現了輸入輸出線程的交替執行
class Resource extends Object{String name;String sex ;boolean flag = false;}class Input implements Runnable{Resource r;Input(Resource r){this.r = r;}public void run(){int x = 0;while(true){synchronized (r) {if(r.flag){try {r.wait();} catch (Exception e) {// TODO: handle exception}}if(x==0){r.name = "BLF";r.sex = "male";}else {r.name = "妮妮妮妮";r.sex = "female";}r.flag = true;r.notify();x = (++x)%2;}}}}class Output implements Runnable{Resource r;public Output(Resource r) {// TODO Auto-generated constructor stubthis.r = r;}public void run() {while(true){synchronized (r) {if(!r.flag){try {r.wait();} catch (Exception e) {// TODO: handle exception}}System.out.println(r.name+"..."+r.sex);r.flag = false;r.notify();}}}}public class Main {public static void main(String[] args){Resource r = new Resource();Input in = new Input(r);Output out = new Output(r); Thread t1 = new Thread(in);Thread t2 = new Thread(out);t1.start();t2.start();}}
三:
等待喚醒機制代碼最佳化
資源中的成員為了可控,應該為私人的,對外提供方法
所以同步的操作,就在Resource類中執行,應用同步函數
class Resource extends Object{private String name;private String sex ;private boolean flag = false;public synchronized void set(String name,String sex) {// TODO Auto-generated constructor stubif(flag){try {this.wait();} catch (Exception e) {// TODO: handle exception}}this.name = name;this.sex = sex;flag = true;this.notify();}public synchronized void out(){if(!flag){try {this.wait();} catch (Exception e) {// TODO: handle exception}System.out.println(name+" : "+sex);flag = false;this.notify();}}}class Input implements Runnable{Resource r;Input(Resource r){this.r = r;}public void run(){int x = 0;while(true){if(x==0){r.set("BLF", "male"); }else {r.set("妮妮妮妮", "female");}x = (++x)%2;}}}class Output implements Runnable{Resource r;public Output(Resource r) {// TODO Auto-generated constructor stubthis.r = r;}public void run() {while(true){r.out();}}}public class Main {public static void main(String[] args){Resource r = new Resource();Input in = new Input(r);Output out = new Output(r); Thread t1 = new Thread(in);Thread t2 = new Thread(out);t1.start();t2.start();}}
JAVA中為實現多線程之間的通訊,需要使用下列哪種流才合適
D,管道流,此流一般用於多線程
JAVA多線程同步的問題
很簡單,當不使用的時候,所有的線程都在運行這一個run,因為你沒鎖,也就是說沒鎖的聽況下,看似只有一個runnable對象,一個run代碼塊,但是四個線程都在用,這相當於一個房間裡面有100本書,是個人都可以同時進入拿,自然四個線程都參與,當你鎖起來的時候就相當於一個人進入了就鎖了門,其他人都進不去了,只有當那個人出來了,其他人才可以進去,但是,裡面是死迴圈,知道書被那個人都裝進麻袋了猜背出來,其他人進去了已經沒有了,所以迴圈直接退出。這樣就看似只有一個線程在執行了。