多線程__【線程間通信】【等待喚醒機制】【多生產多消費】【Lock&Condition接口】

---------------------------------------- android培訓java培訓、期待與您交流! ------------------------------------

線程間通訊:

多個線程在處理同一資源,但是任務卻不同;

一:wait()  notify() notifyAll()用來操作線程的,定義在Object類中

1.這些方法存在同步中

2.使用這些方法必須要標示所屬的同步鎖

3.這些都是監視器的方法,監視器其實就是鎖,鎖可以是任意對象,任意的對象調用的方式一定定義在Object中。

二:wait和sleep的區別

都會拋出異常,需要處理,都會使線程進入凍結狀態

1.wait():釋放資源,釋放鎖,需要被notify()喚醒。

2.sleep():釋放資源,不釋放鎖,休息一段時間醒來後繼續執行。

3.yield():釋放鎖,使當前線程馬上回到就緒狀態,也可以馬上執行。

暫停當前正在執行的線程,並執行其他線程

在同步代碼塊中,只要有特有對象都可以作爲同步鎖,因爲要保證鎖的唯一性。

 

 

等待喚醒機制

使用標記flag,使生產線程和消費線程交替執行並執行完畢後喚醒對方

單一同步只能保證對象屬性的正確,不能保證輸入與輸出一對一,加入等待喚醒後才能夠實現單輸入與單輸出一對一,但不能解決多輸入多輸出的協調。

單生產和單消費實例:

package thread;

public class ProducerConsumer {
	public static void main(String[] args) {
		//創建資源對象
		Resource r = new Resource();
		Producer producer = new Producer(r);
		Consumer consumer = new Consumer(r);

		Thread t = new Thread(producer);
		Thread t2 = new Thread(consumer);
		t.start();
		t2.start();

	}
}

class Resource {
	private String name;//要生產的東西的名字
	private int count = 1;//初始值爲1
	private boolean flag = false;//默認爲沒有

	public synchronized void set(String name) {
		// 如果有饅頭就進入等待狀態,通知消費者來吃
		if (flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		//如果flag = flase就生產饅頭
		this.name = name + count;
		count++;
		System.out.println(Thread.currentThread().getName() + ".....生產者....."
				+ this.name);
		//生產完後設置flag爲true,則自己進入等待狀態,然後喚醒對方起來吃
		flag = true;
		this.notifyAll();
	}

	public synchronized void out() {
		// 如果沒有饅頭就等待
		if (!flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		//flag等於true等於有饅頭,我們就起來吃
		System.out.println(Thread.currentThread().getName()
				+ "..........消費者..........." + this.name);
		//吃完設置flag = false,自己進入等待狀態,喚醒消費者,等饅頭出來了在起來吃。
		flag = false;
		this.notify();
	}
}

class Producer implements Runnable {
	private Resource r;

	public Producer(Resource r) {
		this.r = r;
	}

	@Override
	public void run() {
		while (true) {
			r.set("饅頭");
		}
	}

}

class Consumer implements Runnable {
	private Resource r;

	public Consumer(Resource r) {
		this.r = r;
	}

	@Override
	public void run() {
		while (true) {
			r.out();
		}
	}
}

多生產者 多消費者的問題

while...notifyAll()

相對於單生產單消費的問題,多生產多消費的情況會每次notify()喚醒的線程都不確定,有可能是本方也有可能是對方,會發生  凍結狀態導致的不同步,不能對應輸出

所以每次線程被喚醒後執行時都要對標記進行判斷,如果喚醒的是本方線程,由於標記未改變仍然不能執行。從而保證雙方線程交替執行

但是使用while標記判斷如果只隨機notify()隨機喚醒了一個線程,一旦喚醒的是本方線程,就會導致線程全部處於臨時阻塞狀態(沒有了執行權),所以使用while時要使用notifyAll()

示例:

while(flag)//標記判斷   
 try{this.wait();}  
   catch (Exception e){
}  
要執行的程序方法體;   
flag = true;//改變標記   
this.notifyAll();//喚醒全部  

JDK5.0新增的兩個接口

功能更加強大的鎖,更加直觀的顯式鎖,將鎖進行的對象的封裝,並且將鎖的操作也封裝成對象。

lock接口替代synchronized同步鎖,

Condition接口替代Object監視器方法

wait() notify() notifyAll()

await() signal() signalAll()

 

從Lock 創建鎖從lock接口的方法newCondition()獲取實例來操作鎖,Lock的強大之處在於能夠獲取多個實例來操作同一個鎖,可以用來對指定任務進行控制。

await() 或者signal()對方(指定的任務),不用喚醒全部,本方只喚醒對方

private Lock lock = new ReentrantLock();// 創建鎖
	private Condition condition_pro = lock.newCondition();// 通過鎖獲取Condition實例
	private Condition condition_con = lock.newCondition();// 一鎖多用,相互喚醒


 

多生產多消費升級方案:

package thread;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Resource1 {
	private Lock lock = new ReentrantLock();// 創建鎖
	private Condition condition_pro = lock.newCondition();// 通過鎖獲取Condition實例
	private Condition condition_con = lock.newCondition();// 一鎖多用,相互喚醒
	private String name;
	private int count = 1;
	private boolean flag = false;

	public void set(String name) throws InterruptedException {
		lock.lock();// 加鎖,相當於同步塊或同步函數
		try {
			while (flag) {
				// 如果有饅頭,生產者進入等待狀態
				condition_pro.await();
			}

			this.name = name + count++;
			System.out.println(Thread.currentThread().getName()
					+ "...............生產了................" + this.name);
			// 生產完饅頭以後將flag設置爲true,歡迎消費者。
			flag = true;
			condition_con.signal();// 這樣的結構清晰多了

		} finally {
			lock.unlock();// 在finally中施放鎖
		}
	}

	public void out() throws InterruptedException {
		lock.lock();
		try {
			while (!flag) {// 如果沒有饅頭.消費者進入等待狀態
				condition_con.await();
			}
			System.out.println(Thread.currentThread().getName()
					+ ".........消費了........" + this.name);
			flag = false;
			condition_pro.signal();

		} finally {
			lock.unlock();
		}

	}
}

class Producer1 implements Runnable {
	private Resource1 r;

	public Producer1(Resource1 r) {
		super();
		this.r = r;
	}

	@Override
	public void run() {
		while (true) {
			try {
				r.set("包子");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

class Consumer1 implements Runnable {
	private Resource1 r;

	public Consumer1(Resource1 r) {
		super();
		this.r = r;
	}

	public Resource1 getR() {
		return r;
	}

	public void setR(Resource1 r) {
		this.r = r;
	}

	@Override
	public void run() {
		while (true) {
			try {
				r.out();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

}

public class ProducerConsumerDemo {
	public static void main(String[] args) {
		Resource1 r = new Resource1();
		Producer1 producer = new Producer1(r);
		Consumer1 consumer = new Consumer1(r);
		Thread t = new Thread(producer);
		Thread t2 = new Thread(producer);
		Thread t3 = new Thread(consumer);
		Thread t4 = new Thread(consumer);
		t.start();
		t2.start();
		t3.start();
		t4.start();

	}

}

一定在要finally{}中釋放鎖

代碼優化小結

應用封裝,即將有共性的內容封裝起來,在需要的時候調用。

善用匿名對象對於直接使用的對象參數採用匿名減少代碼量

善用接口類型和類類型的參數傳遞

 

請問java中的lock和synchronized區別是什麼?

主要相同點:Lock能完成synchronized所實現的所有功能

主要不同點:Lock能有比synchronized更精確的語義和更好的性能。synchronized會自動釋放鎖,而Lock要求程序員手工釋放,在finally{}快中


---------------------------------------- android培訓java培訓、期待與您交流! ------------------------------------

 

 

 

 

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