概述
在中國併發這個詞已經非常常見了,比如雙十一購物狂歡節的淘寶,過年回家的春運12306,這都是我們生活中的併發。在短時間內大量集中的訪問一個服務器。然而Java又是解決併發的一把好手,下面簡單介紹一下多線程案例的經典案例,生產者和消費者
。
多線程通信圖解
如何編寫一個多線程配合的代碼
- 線程操作資源類。定義一個資源類,並在資源類中完成相應的方法,將資源類作爲成員變量傳遞進線程中進行操作。
- 如何編寫資源類中的方法,判斷,幹活,通知。先判斷現在是不是該我幹活了,如果是那麼我就幹活,幹完活則通知另外一位工作者來幹活。
生產者消費者案例三種實現方式
- 同步方法實現
- 同步代碼塊實現
- 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();
}
}
注意事項
- 在前兩種方式中
wait
和notify
和notifyAll
方法必須寫在同步代碼塊中。 - 在判斷是否工作時最好使用
while
進行判斷,防止線程的虛假喚醒。 - 在前兩種方式中,喚醒都是隨機喚醒的,而在第三中方式中喚醒的帶有準確性喚醒的,一對一喚醒,不存在隨機性。
代碼寫完,小弟知識淺薄,各路大神如果看出問題,可以幫我指正,我及時修改!!!