Java多線程案例生產者消費者

概述

在中國併發這個詞已經非常常見了,比如雙十一購物狂歡節的淘寶,過年回家的春運12306,這都是我們生活中的併發。在短時間內大量集中的訪問一個服務器。然而Java又是解決併發的一把好手,下面簡單介紹一下多線程案例的經典案例,生產者和消費者

多線程通信圖解

多線程通信圖解

如何編寫一個多線程配合的代碼

  1. 線程操作資源類。定義一個資源類,並在資源類中完成相應的方法,將資源類作爲成員變量傳遞進線程中進行操作。
  2. 如何編寫資源類中的方法,判斷,幹活,通知。先判斷現在是不是該我幹活了,如果是那麼我就幹活,幹完活則通知另外一位工作者來幹活。

生產者消費者案例三種實現方式

  • 同步方法實現
  • 同步代碼塊實現
  • lock鎖實現

代碼實現一:synchronized方法

/**
* 描述資源類
*/
public class factory {

	/**用來判斷是否需要生產的標誌,同時模擬資源*/
	private int repertory = 0;

	/**
	* 工廠的生產方法
	*/
	public synchronized void product() {
	
		// 判斷是否需要生產,如果不需要生產則進入等待狀態
		while(repertory != 0) {
			try{
				this.wait();
			} catch(InterruptedException e) {
				e.printStackTrace();
			}
		}
		repertory++;
		System.out.println(Thread.currentThread().getName() + "生產," + "目前庫存:" + repertory);
		// 喚醒消費者
		this.notifyAll();
	}

	public synchronized void consumer() {
		
		// 判斷是否可以消費,如果不可以消費,則進入wait狀態
		while(repertory == 0) {
			try{
				this.wait();
			} catch(InterruptedException e) {
				e.printStackTrace();
			}
		}

		repertory--;
		System.out.println(Thread.currentThread().getName() + "消費," + "目前庫存:" + repertory);
		// 喚醒生產者
		this.notifyAll();
	}
}

代碼實現二:synchronized代碼塊

public class Factory {

	/**用來判斷是否需要生產的標誌,同時模擬資源*/
	private int repertory = 0;

	/**
	* 生產方法
	*/
	public void product() {
		synchronized(this) {
		
			// 判斷是否需要生產,如果不需要生產則進入等待狀態
			while(repertory != 0) {
				try{
					this.wait();
				} catch(InterruptedException e) {
					e.printStackTrace();
				}
			}
			repertory++;
			System.out.println(Thread.currentThread().getName() + "生產," + "目前庫存:" + repertory);
			// 喚醒消費者
			this.notifyAll();
		}
	}
	
	public void consumer() {
		synchronized(this) {
			while(repertory == 0) {
				try{
					this.wait();
				} catch(InterruptedException e) {
					e.printStackTrace();
				}
			}
			repertory--;
			System.out.println(Thread.currentThread().getName() + "消費," + "目前庫存:" + repertory);
			// 喚醒生產者
			this.notifyAll();
		}
	}
}

代碼實現三:lock鎖

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

public class Factory {

	/**用來判斷是否需要生產的標誌,同時模擬資源*/
	private int repertory == 0;
	
	/**定義鎖*/
	private final Lock lock = new ReetrantLock();

	/**控制生產方法運行*/
	private final Condition product = lock.newCondition();

	/**控制消費方法運行*/
	private final Condition consumer = lock.newCondition();

	public void product() {
		lock.lock();
		try{
			// 判斷是否需要生產,如果不需要生產則停止生產
			while(repertory != 0) {
				try{
					product.await();
				} catch(InterruptedException e) {
					e.printStackTrace();
				}
			}
			repertory++;
			System.out.println(Thread.currentThread().getName() + "生產," + "目前庫存:" + repertory);
			// 喚醒消費者
			consumer.signal();
			
		} finally{
			lock.unlock();
		}
	}

	public void consumer() {
		lock.lock();
		try{
			while(repertory == 0) {
				try{
					consumer.await();
				} catch(interruptedException e) {
					e.printStackTrace();
				}
			}
			repertory--;
			System.out.println(Thread.currentThread().getName() + "消費," + "目前庫存:" + repertory);
			// 喚醒生產者
			product.signal();
		} finally {
			lock.unlock();
		}
		
	}
}

測試類

public class Demo {

	public static void main(String[] args) {
		Factory goods = new Factory ();
		new Thread(() -> {
			for (int i = 0; i < 100; i++) {
				try {
					TimeUnit.SECONDS.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				goods.product3();
			}
		}, "生產者1").start();
		
		new Thread(() -> {
			for (int i = 0; i < 100; i++) {
				try {
					TimeUnit.SECONDS.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				goods.consumer3();
			}
		}, "消費者1").start();
		
		new Thread(() -> {
			for (int i = 0; i < 100; i++) {
				try {
					TimeUnit.SECONDS.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				goods.product3();
			}
		}, "生產者2").start();
		
		new Thread(() -> {
			for (int i = 0; i < 100; i++) {
				try {
					TimeUnit.SECONDS.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				goods.consumer3();
			}
		}, "消費者2").start();
	}
}

注意事項

  1. 在前兩種方式中waitnotifynotifyAll方法必須寫在同步代碼塊中。
  2. 在判斷是否工作時最好使用while進行判斷,防止線程的虛假喚醒。
  3. 在前兩種方式中,喚醒都是隨機喚醒的,而在第三中方式中喚醒的帶有準確性喚醒的,一對一喚醒,不存在隨機性。

代碼寫完,小弟知識淺薄,各路大神如果看出問題,可以幫我指正,我及時修改!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章