線程間的通信:其實就是多個線程操作同一資源,並且操作的動作不一樣,最簡單的例子:輸入輸出姓名、性別信息。
線程同步保證了不會出現名字與性別對性錯誤的情況,等待喚醒機制保證了不會出現一次輸入多次輸出或多次輸入一次輸出的情況。等待、喚醒必須是同一個鎖,喚醒機制職能喚醒跟它持有相同鎖的處於等待狀態的線程。
輸入輸出姓名、性別信息的例子:
/*
線程間通信,其實就是多個線程操作同一個資源。
但操作的動作不同。
名詞提煉法:資源、兩個不同的操作
比如:一個線程往裏面輸入名字和性別,另一個線程取出名字和線程,分別簡稱爲輸入線程和輸出線程。
爲了保證輸入線程和輸出線程操作的是同一資源,資源對象應以參數的形式傳遞,需要達到的目
的是:輸入線程輸入一個,輸出線程將其輸出,爲避免發生輸入線程只輸入了名字
Cpu執行權就被輸出線程搶走,以致輸出錯誤的信息,應將使用線程同步;爲避免輸入線程輸入一
次,輸出線程輸出多次(或輸入線程輸入多次,而輸出線程只輸出一次)的情況,應定義一個標誌
位來判斷是否可輸入或可輸出,在不可輸入或不可輸出時,先讓線程等待,等被喚醒時判斷標
志位狀態。
步驟:1、定義一類描述資源:名字、性別、標誌位;
2、定義Input類,實現Runnable接口,一初始化就有了資源對象,複寫run方法;
3、定義Output類,實現接口Runnable,一出書畫就有了字眼對象,複寫run方法;
4、通過Thread類建立線程;
5、分別將Input、Output的子類對象以參數的形式傳遞給Thread類的構造函數;
6、調用線程的start類開啓線程。
其他說明:這個是最簡單的等待喚醒機制的體現,通俗的說,你操作一下,我操作一下,我操作結束
,進入等待之前會喚醒你,你操作結束,進入等待之前也要喚醒我。
等待喚醒機制:只能喚醒使用同一個鎖的處於等待狀態的線程。
wait()、notify()、notifyAll(),只能在同步代碼中才能使用,並且使用的時候要加上
該線程所屬的鎖(如:r.wait()、this.wait()),而鎖是任意對象,所以這些方法被定義
在Object類中。wait()有申明異常,在Runnable的run方法沒有拋異常,所以只能try處理,
不能拋。
關鍵點:1、定義標誌位來判斷是否可輸入或可輸出;
2、根據一個變量的值來選擇輸入的信息;(int i = 0 ;i = (i+1)%2;可以使i的值在0、1之間變換)
3、線程執行的代碼一般都是循環體;
4、資源對象以參數的形式傳遞可以保證,輸入和輸出操作的是同一對象。
*/
//定義描述資源的類
class Resource
{
String name;
String sex;
boolean flag = false;
}
//定義Input類
class Input implements Runnable
{
private Resource r;
Input(Resource r)
{
this.r = r;
}
public void run()
{
int i = 0;
while(true)
{
synchronized(r)
{
if(r.flag)
try{r.wait();}catch(Exception e){}
if(i == 0)
{
r.name = "mack";
r.sex = "man";
}
else
{
r.name = "莉莉";
r.sex = "女女";
}
i = (i+1)%2;
r.flag = true;
r.notify();
}
}
}
}
//定義Output類
class Output implements Runnable
{
private Resource r;
Output(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
synchronized(r)
{
if(!r.flag)
try{r.wait();}catch(Exception e){}
System.out.println(r.name+"------"+r.sex);
r.flag = false;
r.notify();
}
}
}
}
class InputOutput
{
public static void main(String[] args)
{
//創建資源對象
Resource r = new Resource();
//將資源對象傳遞給Input和Output
Input in = new Input(r);
Output ot = new Output(r);
//創建線程
Thread t1 = new Thread(in);
Thread t2 = new Thread(ot);
//開啓線程
t1.start();
t2.start();
}
}
生產消費問題:
/*
生產消費模型:生產一個消費一個,並且是多個線程生產,多個線程消費。
名詞提煉法:資源、生產操作、消費操作。
判斷標誌if改while,是避免重複生產或重複消費的問題,用了while循環判斷之後會出現
所有線程都處於等待狀態,所以需要將notify改爲notifyAll。
注:notifyAll喚醒的是全部處於等待狀態的線程,而不僅僅是對方線程。
*/
//定義資源類
class Res
{
String name;
int count = 0;
String toString;
boolean flag = false;
//生產的過程
public synchronized void set(String name)
{
while(flag)
try{this.wait();}catch(Exception e){}
toString = name+"----"+count++;
System.out.println("生產++++"+toString);
this.notifyAll();
flag = true;
}
//消費的過程
public synchronized void get()
{
while(!flag)
try{this.wait();}catch(Exception e){}
System.out.println("消費===="+toString);
this.notifyAll();
flag = false;
}
}
//定義生產類
class Product implements Runnable
{
private Res r;
Product(Res r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.set("子弟土豆片");
}
}
}
//定義消費類
class Consumer implements Runnable
{
private Res r;
Consumer(Res r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.get();
}
}
}
class ProConTest
{
public static void main(String[] args)
{
//創建資源對象
Res r = new Res();
//創建生產、消費對象,並將資源對象傳入
Product pro = new Product(r);
Consumer con = new Consumer(r);
//通過Thread類創建線程
Thread tp1 = new Thread(pro);
Thread tp2 = new Thread(pro);
Thread tc1 = new Thread(con);
Thread tc2 = new Thread(con);
//開啓並執行該線程的方法
tp1.start();
tp2.start();
tc1.start();
tc2.start();
}
}