java線程(三) 生產者消費者模式-線程同步問題

來源:互聯網
上載者:User

引言

  生產者和消費者問題是執行緒模式中的經典問題:生產者和消費者在同一時間段內共用同一個儲存空間,如所示,生產者向空間裡存放資料,而消費者取用資料,如果不加以協調可能會出現以下情況:

生產者消費者圖

  儲存空間已滿,而生產者佔用著它,消費者等著生產者讓出空間從而去除產品,生產者等著消費者消費產品,從而向空間中添加產品。互相等待,從而發生死結。

JAVA解決執行緒模式的三種方式

1、wait和notify

package com.ch.egg;import java.util.ArrayList;import java.util.List;public class ThreadEgg {private List<Object> eggs = new ArrayList<Object>();/** * 若不擁有對象的鎖標記(synchronized),而試圖用wait/notify協調共用對象資源, * 應用程式將拋出IllegalMonitorStateException。 * * @param i * @return */public synchronized Object getEggs(int i) {//若盤裡雞蛋為0,則wait()阻塞進入隊列,等待生產者(放雞蛋)線程喚醒if(eggs.size() == 0) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}Object egg = eggs.get(0);eggs.clear();notify();System.out.println("拿到  雞蛋"+i);return egg;}public synchronized void pugEgg(Object egg,int i) {//若盤裡雞蛋為1,則wait()阻塞進入隊列,等待消費者(取雞蛋)線程喚醒if(eggs.size() > 0) {try {wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}eggs.add(egg);notify();System.out.println("放入  雞蛋"+i);}static class AddThread extends Thread {private ThreadEgg threadEgg;private Object egg ;public AddThread(ThreadEgg threadEgg) {this.threadEgg = threadEgg;}public void run() {for(int i = 0;i<5;i++){threadEgg.pugEgg(egg,i);}}}static class GetThread extends Thread{   private ThreadEgg threadEgg;public GetThread(ThreadEgg threadEgg) {this.threadEgg = threadEgg;}                   public void run(){               for(int i=0;i<5;i++){               threadEgg.getEggs(i);               }           }       } public static void main(String[] args) {try {ThreadEgg threadEgg = new ThreadEgg();AddThread aTh = new AddThread(threadEgg);GetThread gTh = new GetThread(threadEgg);aTh.start();gTh.start();aTh.join();gTh.join();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("over......");}}

2、await()和signal(),即線程鎖的方式

package com.ch.egg;import java.util.LinkedList;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/** * ReentrantLock 是一個可重新進入的互斥鎖 Lock, * 它具有與使用 synchronized 方法和語句所訪問的隱式監視器鎖相同的一些基本行為和語義,但功能更強大 *  * Condition 將 Object 監視器方法(wait、notify 和 notifyAll)分解成截然不同的對象, * 以便通過將這些對象與任意 Lock 實現組合使用,為每個對象提供多個等待 set(wait-set)。 * 其中,Lock 替代了 synchronized 方法和語句的使用,Condition 替代了 Object 監視器方法的使用。 *  * @author ch * */public class ThreadEggLock {private LinkedList<Object> eggs = new LinkedList<Object>();private int MAX = 1;private final Lock lock = new ReentrantLock();private final Condition full = lock.newCondition();private final Condition empty = lock.newCondition();private int count = 0;public void start() {new Producer().start();new Consumer().start();}public static void main(String[] args) throws Exception {ThreadEggLock s2 = new ThreadEggLock();s2.start();}class Producer extends Thread {public void run() {while (count < 5) {lock.lock();try {while (eggs.size() == MAX) { //盤子裡滿了System.out.println("------警告:盤子裡滿了!");full.await();}Object egg = new Object();if (eggs.add(egg)) {System.out.println("放入  雞蛋" + count);empty.signal();}} catch (InterruptedException ie) {System.out.println("producer is interrupted!");} finally {lock.unlock();}}}}class Consumer extends Thread {public void run() {while (count <= 5) {lock.lock();try {while (eggs.size() == 0) { //盤子是空的System.out.println("------警告:現在盤子是空的");empty.await();}eggs.removeLast();System.out.println("拿走  雞蛋" + count);count++;full.signal();} catch (InterruptedException ie) {System.out.println("consumer is interrupted!");} finally {lock.unlock();}}}}}

3、阻塞隊列的方式

package com.ch.egg;import java.util.concurrent.LinkedBlockingQueue;public class ThreadEggBlock {// 建立一個阻塞隊列/** * 一個基於已連結節點的、範圍任意的 blocking queue。 * 此隊列按 FIFO(先進先出)排序元素。 * 隊列的頭部 是在隊列中時間最長的元素;隊列的尾部 是在隊列中時間最短的元素。 *  * 新元素插入到隊列的尾部,並且隊列擷取操作會獲得位於隊列頭部的元素。 * 連結隊列的輸送量通常要高於基於數組的隊列,但是在大多數並發應用程式中,其可預知的效能要低。  */private LinkedBlockingQueue<Object> queue_eggs = new LinkedBlockingQueue<Object>(1);private int count = 1;public void start() throws InterruptedException {Producer producer = new Producer();Consumer consumer = new Consumer();producer.start();consumer.start();}public static void main(String[] args) throws Exception {ThreadEggBlock s3 = new ThreadEggBlock();s3.start();}class Producer extends Thread {public void run() {while (count < 15) {try {Egg egg = new Egg(String.valueOf(count));queue_eggs.put(egg);System.out.println("放入  雞蛋" + egg.getName());count++;} catch (InterruptedException e) {System.out.println("producer is interrupted!");}}}}class Consumer extends Thread {public void run() {while (count <= 15) {try {// 取出一個對象Egg egg = (Egg) queue_eggs.take();System.out.println("取出  雞蛋" + egg.getName());} catch (InterruptedException e) {System.out.println("producer is interrupted!");}}}}class Egg {private String name;public Egg(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}}}

總結:

1、處理多線程同步與互斥問題非常重要,著名的生產者-消費者例子就是一個經典的例子,是任何語言多線程必學的例子。

2、三種方式實現同步的原理一致,都是對獨佔空間加鎖,阻塞和喚醒線程;第一種方式比較傳統,第二種方式最靈活,第三種方式最簡單,只需儲存和取用,線程同步的操作交由LinkedBlockingQueue全權處理。 

線程同步小結

1、線程同步的目的是為了保護多個線程訪問一個資源時對資源的破壞。2、線程同步方法是通過鎖來實現,每個對象都有且僅有一個鎖,這個鎖與一個特定的對象關聯;線程一旦擷取了對象鎖,其他訪問該對象的線程就無法再訪問該對象的其他同步方法。3、對於靜態同步方法,鎖是針對這個類的,鎖對象是該類的Class對象。靜態和非靜態方法的鎖互不干預。一個線程獲得鎖,當在一個同步方法中訪問另外對象上的同步方法時,會擷取這兩個對象鎖。4、對於同步,要時刻明白是在哪個對象上同步。

5、在使用synchronized關鍵字時候,應該儘可能避免在synchronized方法或synchronized塊中使用sleep或者yield方法,因為synchronized程式塊佔有著對象鎖,你休息那麼其他的線程只能一邊等著你醒來執行完了才能執行。不但嚴重影響效率,也不合邏輯。同樣,在同步程式塊內調用yeild方法讓出CPU資源也沒有意義,因為你佔用著鎖,其他互斥線程還是無法訪問同步程式塊。當然與同步程式塊無關的線程可以獲得更多的執行時間。6、編寫安全執行緒的類,需要時刻注意對多個線程競爭訪問資源的邏輯和安全做出正確的判斷,對“原子”操作做出分析,並保證原子操作期間別的線程無法訪問競爭資源。7、當多個線程等待一個對象鎖時,沒有擷取到鎖的線程將發生阻塞。8、死結是線程間相互等待鎖鎖造成的,一旦程式發生死結,程式將死掉。 java線程深度解析:http://lavasoft.blog.51cto.com/62575/27069

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.