Java 多線程編程 生產者 消費者模式
synchronized和 ReentrantLock的相同點
- 兩種鎖都是獨佔鎖, 只允許線程互斥的訪問臨界區
- 兩種鎖都可以重入, 在一個線程可以重複獲取多次鎖
兩者區別是 synchronized重入不用考慮解鎖相關問題, 而 ReentrantLock必須確保重複獲取鎖的次數和釋放鎖的次數相同, 否者相關鎖無法解
synchronized和 ReentrantLock的區別
- synchronized是加解鎖是隱式的, ReentrantLock是手動加解鎖
- synchronized是通過 Object的 wait和 notify方法來實現線程間的等待通知機制, 但是 ReentrantLock是通過 Condition接口來實現了等待通知機制
- ReentrantLock支持響應中斷, 出現死鎖現象可以通過 lockInterruptibly()方法響應中斷. 但是 synchronized出現死鎖時會無限等待,
- ReentrantLock支持公平鎖, 被鎖後等了最長時間的線程將會優先解鎖, 而默認是非公平鎖就是隨機的 synchronized只支持非公平鎖
- 非公平鎖性能優於公平鎖, 但是公平鎖能防止線程飢餓現象發生. 創建方式: new ReentrantLock(true) 參數 true是創建公平鎖, false或無參是非公平鎖
1. 消息類
- 消息數超過存放最大數, 則生產者線程阻塞(等待消費者消費後喚醒)
- 消息數等於空, 則消費者線程阻塞(等待生產者生產後喚醒)
public class Message {
/** 當前消息數量*/
private int count = 0;
/** 信息存放最大限數*/
private int maximum = 20;
/** 重入鎖*/
private Lock lock;
/** 生產者鎖控制器*/
private Condition producerCondition;
/** 消費者鎖控制器*/
private Condition consumerCondition;
public Message() {}
public Message(final Lock lock, final Condition producerCondition, final Condition consumerCondition) {
this.lock = lock;
this.producerCondition = producerCondition;
this.consumerCondition = consumerCondition;
}
/**
* 生產消息
* */
public void set() {
/** 獲取鎖*/
lock.lock();
try {
if (count <= maximum) {
/** 生產一個消息*/
System.out.println("生產者 線程" + Thread.currentThread().getName() + "生產了一個消息, 當前有" + (++count) + "個消息");
/** 喚醒等待的消費者*/
consumerCondition.signal();
} else {
try {
/**
* 如果當前消息大於 maximum信息最大數
* 生產者進入睡眠/等待狀態
* */
producerCondition.await();
System.out.println("生產者 線程" + Thread.currentThread().getName() + "進入睡眠");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
/** 釋放鎖*/
lock.unlock();
}
}
/**
* 消費消息
* */
public void get() {
/** 獲取鎖*/
lock.lock();
try {
if (count > 0) {
/** 消費一個消息*/
System.out.println("消費者 線程" + Thread.currentThread().getName() + "消費了一個消息, 當前有" + (--count) + "個消息");
/** 喚醒等待的生產者*/
producerCondition.signal();
} else {
try {
/** 如果沒有消息, 消費者進入睡眠/等待狀態*/
consumerCondition.await();
System.out.println("消費者 線程" + Thread.currentThread().getName() + "進入睡眠");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
/** 釋放鎖*/
lock.unlock();
}
}
}
2. 生產者類
public class Producer implements Runnable {
private Message message;
public Producer(Message message) {
this.message = message;
}
@Override
public void run() {
while(true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
message.set();
}
}
}
3. 消費者類
public class Consumer implements Runnable {
private Message message;
public Consumer(Message message) {
this.message = message;
}
@Override
public void run() {
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
message.get();
}
}
}
Runnable是接口, Thread是 Runnable的實現類, 由於 Java對多接口實現比較靈活, 開發多線程時建議實現 Runnable接口. 啓動時用 Thread的 start(). 例: new Thread().start()
4. App.java
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class App {
public static void main(String[] args) {
/** 重入鎖*/
final Lock lock = new ReentrantLock();
/** 生產者鎖控制器*/
final Condition producerCondition = lock.newCondition();
/** 消費者鎖控制器*/
final Condition consumerCondition = lock.newCondition();
final Message message = new Message(lock, producerCondition, consumerCondition);
/** 建幾個生產線程*/
new Thread(new Producer(message)).start();
new Thread(new Producer(message)).start();
new Thread(new Producer(message)).start();
/** 建幾個消費線程*/
new Thread(new Consumer(message)).start();
new Thread(new Consumer(message)).start();
new Thread(new Consumer(message)).start();
new Thread(new Consumer(message)).start();
}
}
如果您覺得有幫助,歡迎點贊哦 ~ 謝謝!!