Java基礎--併發編程基礎(3)

線程間通信

爲什麼要進行線程間通信?有一個很經典的例子就是生產者-消費者案例(簡化後):必須是生產者生產一個,消費者消費一個,沒有生產不能消費,沒有消費不能生產。生產者和消費者分別是兩個不同的線程,那這兩個線程在執行的時候必須相互通信才能按照要求執行下去:生產者生產前先看看消費者消費完了沒?消費完了再生產,否則不生產,生產好了之後,告訴消費者你來消費;同樣的,消費者消費前先看看你生產了沒?生產了就消費,否則不消費,消費好了之後,告訴生產者你來生產。這就是典型的線程間通信問題。
首先要明白的是:線程通信的基礎是線程同步,如果線程間連同步都不用的話,就更沒有通信的必要了。線程同步是對互斥資源(說互斥不太嚴謹,是需要控制訪問的資源,不一定是互斥)的同步訪問,而線程間通信是線程間運行進度的相互影響。
線程間通信即線程間相互影響,相互影響無非是操作資源影響和運行進度影響,資源影響由同步和進度影響共同造成,進度影響即是對線程進行手動的狀態間轉換。
Java中線程間通信是使用wait()、notify()、notifyAll()這三個方法完成的(除了這基本的線程間通信的方式,後續博文還會探討其他線程間通信的方式)。
wait()方法是Object類的不可重寫方法,那也就是說每個對象都有這個方法哦。調用wait方法後,持有當前對象監視器的線程交出監視器,並且掛起,等待被喚醒(當然也有可能你沒有喚醒它,它卻由於假喚醒醒了)
notify()方法是Object類的不可重寫方法,也是每個類都有這個方法。調用notify方法後,喚醒一個等待持有該對象監視器的任意(隨機的哦)一個線程
notifyAll()方法是Object類的不可重寫方法,每個類都有。調用這個方法後,喚醒所有等待持有該對象監視器的線程
未使用線程間通信的生產者-消費者:
public class ProducerCustomerWithoutCommunication {

	public static void main(String[] args) {
		Queue q = new Queue();
		q.n = 0;
		Thread t1 = new Thread(new Producer(q));
		t1.start();
		Thread t2 = new Thread(new Customer(q));
		t2.start();
		
		/*
		 * 想要實現的效果是:生產一個拿走一個,生產一個拿走一個,即生產完一個如果沒有拿走的話,不再生產,如果生產者還沒生產出來,則不拿
		 */
		//使用線程間通訊之前
//		Set: 1
//		Set: 2
//		Set: 3
//		Get: 3
//		Get: 4
//		Get: 4
//		Get: 4
//		Get: 4
//		Get: 4
//		Get: 4
//		Set: 4
//		Set: 5
//		Get: 4
		/*
		 * 沒有達到效果,因爲連續生產了三次後纔開始消費,然後連續消費了
		 * 更過分的是生產者還沒有生產完(顯然已經生產了4)4,消費者已經拿到4了
		 */
	}

}
class Queue{
	int n;  
	/*
	 * 這裏加同步是爲了保證每次要麼只能生產要麼只能消費,不能這裏生產沒完呢就開始消費也不能消費沒完成就開始生產
	 * 但是,根據輸出:4還沒生產完就被消費了,並不是這裏的問題,而是其他地方的問題?發現了嗎?
	 */
	public synchronized void get(){
		System.out.println("Get: "+n);
	}
	public synchronized void set(int n){
		this.n = n;
		System.out.println("Set: "+n);
	}
}
class Producer implements Runnable{
	Queue q;
	Producer(Queue q){
		this.q = q;
	}
	@Override
	public void run(){
		/*
		 * 線程不能被殺死,只能通過設置標誌位的方式使其終結,即讓run方法返回後這個線程就終結了
		 */
		while(q.n<5&&q.n>-5){
			int n = q.n;
			n++;
			q.set(n);
		}
	}
}
class Customer implements Runnable{
	Queue q;
	public Customer(Queue q){
		this.q = q;
	}
	@Override
	public void run(){
		while(q.n<5&&q.n>-5){
			q.get();
		}
	}
}
使用了線程間通信的生產者-消費者:
public class ProducerCustomerWithCommunication {

	public static void main(String[] args) {
		Queue2 q = new Queue2();
		Thread t1 = new Thread(new Producer2(q));
		Thread t2 = new Thread(new Customer2(q));
		t1.start();
		t2.start();
		
//		運行結果:
//		Set..1
//		Get..1
//		Set..2
//		Get..2
//		Set..3
//		Get..3
//		Set..4
//		Get..4
//		Set..5
//		Get..5
//		Set..6
//		Get..6
//		Set..7
//		Get..7
//		Set..8
//		Get..8
//		Set..9
//		Get..9
//		Set..10
//		Get..10

	}

}

class Queue2{
	int n = 0;
	boolean isProduced = false;
	public synchronized void set(){
		if(isProduced)
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		n++;
		System.out.println("Set.."+n);
		isProduced = true;
		notify();
	}
	public synchronized void get(){
		if(!isProduced)
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		System.out.println("Get.."+n);
		isProduced = false;
		notify();
	}
}
class Producer2 implements Runnable{
	Queue2 q;
	public Producer2(Queue2 q){
		this.q = q;
	}
	@Override
	public void run(){
		while(q.n<10)
			q.set();
	}
}
class Customer2 implements Runnable{
	Queue2 q;
	public Customer2(Queue2 q){
		this.q = q;
	}
	@Override
	public void run(){
		while(q.n<10)
			q.get();
	}
}

發佈了28 篇原創文章 · 獲贊 39 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章