三.多線程之間通信
1.爲什麼有線程之間的通信?
(1)線程在運行時,cpu是隨機調度的,往往我們會使用多個線程來完成同一個任務(類似多人協作),並且我們希望多線程完成任務是有規律的,那麼在完成任務的過程中,線程之間必然要有一些交流,這樣才能使用同一份數據進行操作。
(2)多線程使用同一份數據來完成任務,必然就會造成資源的爭奪,所以引入線程的通信可以解決這樣一個問題。
2.什麼是線程之間的通信
多線程之間的通信就是多個線程共同操作同一個共享變量,一個線程在佔用共享變量時,通知其他線程進行等待(wait()方法),使用完畢後,將其他線程喚醒(notify()和notifyall()方法)。這就是線程的等待喚醒機制:wait()、notify()。線程之間通信最典型的例子就是生產者與消費者問題。
3.wait、notify 方法
(1)因爲涉及到對象鎖,他們必須都放在synchronized中來使用. Wait()、Notify()一定要在synchronized裏面進行使用。
(2)Wait()必須暫定當前正在執行的線程,並釋放資源鎖,讓其他線程可以有機會運行
(3)notify/notifyall: 喚醒因鎖池中的線程,使之運行
注意:一定要在線程同步中使用,並且是同一個鎖的資源
4.演示生產者和消費者
package thread_demo3;
/**
* 多線程之間通訊,生產者消費者
* @author johson
* 1.因爲涉及到對象鎖,他們必須都放在synchronized中來使用. Wait、Notify一定要在synchronized裏面進行使用。
* 2.Wait必須暫定當前正在執行的線程,並釋放資源鎖,讓其他線程可以有機會運行
* 3. notify/notifyall: 喚醒線程,使之運行
*
*/
//生產的對象
class Person{
String name;
String sex;
//flag==false時,只寫不度
//flag==true時,只讀不寫
boolean flag = false;
}
//生產者進程
class writeThread extends Thread{
public Person person;
public writeThread(Person person) {
this.person = person;
}
@Override
public void run() {
int count = 0;
while(true){
synchronized (person) {
//當flag==true的時候,消費者去讀取person
if(person.flag){
try {
person.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//當flag==false時,生產person,然後通知消費者去讀取
else {
if(count == 0){
person.name = "小紅";
person.sex = "女";
}
else {
person.name = "小軍";
person.sex = "男";
}
System.out.println("生產者生產"+person.name+","+person.sex);
count = (count+1)%2;
person.flag = true;
person.notify();
}
}
}
}
}
//消費者進程
class readThread extends Thread{
public Person person;
public readThread(Person person) {
this.person = person;
}
@Override
public void run() {
while (true) {
synchronized (person) {
//flag==false時,等待生產者生產person
if(!person.flag){
try {
person.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//flag==true時,消費者讀取person,然後將flag變成false,通知生產者去生產person
else {
System.out.println("消費者消費"+person.name+","+person.sex);
person.flag = false;
person.notify();
}
}
}
}
}
public class test1 {
public static void main(String[] args) {
//新建一個person對象
Person person = new Person();
//生產者
writeThread write = new writeThread(person);
//消費者
readThread read = new readThread(person);
write.start();
read.start();
}
}
運行結果如下:
5.wait與sleep區別
(1)對於sleep()方法,我們首先要知道該方法是屬於Thread類中的。而wait()方法,則是屬於Object類中的。
(2)sleep()方法導致了程序暫停執行指定的時間,讓出cpu該其他線程,但是他的監控狀態依然保持者,當指定的時間到了又會自動恢復運行狀態。
(3)在調用sleep()方法的過程中,線程不會釋放對象鎖。
(4)調用wait()方法的時候,線程會放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象調用notify()方法後本線程才進入對象鎖定池準備,獲取對象鎖進入運行狀態。