1. 簡介
生產(chǎn)者 & 消費(fèi)者之間存在 強(qiáng)耦合問題
2. 解決方案
采用 生產(chǎn)者 & 消費(fèi)者 模式,具體介紹如下:
示意圖
3. 具體解決方式介紹
方式1:wait() / notify()
// Object類里的兩個(gè)方法,所有Object子類都可使用這2個(gè)方法
// 對(duì)象的監(jiān)視器對(duì)鎖對(duì)象的鎖定(也就是代碼中的lock對(duì)象),注意是調(diào)用鎖對(duì)象的wait() / nofity()
public class Test {
private static Integer count = 0;
private final Integer FULL = 5;
private static String lock = "lock";
public static void main(String[] args) {
Test t = new Test();
new Thread(t.new Producer()).start();
new Thread(t.new Consumer()).start();
new Thread(t.new Producer()).start();
new Thread(t.new Consumer()).start();
}
class Producer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
synchronized (lock) {
while (count == FULL) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count++;
System.out.println("生產(chǎn)者"+Thread.currentThread().getName()
+ "已生產(chǎn)完成陌粹,商品數(shù)量:" + count);
lock.notifyAll();
}
}
}
}
class Consumer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
synchronized (lock) {
while (count == 0) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
System.out.println("消費(fèi)者"+Thread.currentThread().getName()
+ "已消費(fèi),剩余商品數(shù)量:" + count);
lock.notifyAll();
}
}
}
}
}
// 測試結(jié)果
生產(chǎn)者Thread-0已生產(chǎn)完成福压,商品數(shù)量:1
生產(chǎn)者Thread-2已生產(chǎn)完成掏秩,商品數(shù)量:2
消費(fèi)者Thread-1已消費(fèi),剩余商品數(shù)量:1
消費(fèi)者Thread-3已消費(fèi)荆姆,剩余商品數(shù)量:0
生產(chǎn)者Thread-0已生產(chǎn)完成蒙幻,商品數(shù)量:1
消費(fèi)者Thread-3已消費(fèi),剩余商品數(shù)量:0
生產(chǎn)者Thread-2已生產(chǎn)完成胆筒,商品數(shù)量:1
消費(fèi)者Thread-1已消費(fèi)邮破,剩余商品數(shù)量:0
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:1
生產(chǎn)者Thread-2已生產(chǎn)完成仆救,商品數(shù)量:2
消費(fèi)者Thread-1已消費(fèi)抒和,剩余商品數(shù)量:1
消費(fèi)者Thread-3已消費(fèi),剩余商品數(shù)量:0
生產(chǎn)者Thread-0已生產(chǎn)完成派桩,商品數(shù)量:1
消費(fèi)者Thread-1已消費(fèi),剩余商品數(shù)量:0
生產(chǎn)者Thread-2已生產(chǎn)完成蚌斩,商品數(shù)量:1
消費(fèi)者Thread-3已消費(fèi)铆惑,剩余商品數(shù)量:0
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:1
消費(fèi)者Thread-1已消費(fèi)送膳,剩余商品數(shù)量:0
生產(chǎn)者Thread-2已生產(chǎn)完成员魏,商品數(shù)量:1
消費(fèi)者Thread-3已消費(fèi),剩余商品數(shù)量:0
方式2:await() / signal()
// 對(duì)wait()/notify()的改進(jìn)叠聋,功能更加強(qiáng)大撕阎、更適用于高級(jí)用戶
// synchronized 托管給JVM執(zhí)行
// 而lock是Java寫的控制鎖的代碼
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test {
private static Integer count = 0;// 緩沖區(qū)
private final Integer FULL = 5;
final Lock lock = new ReentrantLock(); // 此處采用 ReentrantLock,獲得可重入鎖
final Condition put = lock.newCondition();
final Condition get = lock.newCondition();
public static void main(String[] args) {
Test t = new Test();
new Thread(t.new Producer()).start();
new Thread(t.new Consumer()).start();
new Thread(t.new Consumer()).start();
new Thread(t.new Producer()).start();
}
// 生產(chǎn)者
class Producer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
//加鎖
lock.lock();
try {
while (count == FULL) {
try {
put.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count++;
System.out.println("生產(chǎn)者" + Thread.currentThread().getName()
+ "已生產(chǎn)完成碌补,商品數(shù)量: " + count);
//通知消費(fèi)者虏束,現(xiàn)在可以消費(fèi)
get.signal();
} finally {
lock.unlock();
}
}
}
}
// 消費(fèi)者
class Consumer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
lock.lock();
try {
while (count == 0) {
try {
get.await();
} catch (Exception e) {
e.printStackTrace();
}
}
count--;
System.out.println("消費(fèi)者" + Thread.currentThread().getName()
+ "已消費(fèi),剩余商品數(shù)量: " + count);
put.signal();
} finally {
lock.unlock();
}
}
}
}
}
// 測試結(jié)果
生產(chǎn)者Thread-3已生產(chǎn)完成厦章,商品數(shù)量: 1
生產(chǎn)者Thread-0已生產(chǎn)完成镇匀,商品數(shù)量: 2
消費(fèi)者Thread-1已消費(fèi),剩余商品數(shù)量: 1
消費(fèi)者Thread-2已消費(fèi)袜啃,剩余商品數(shù)量: 0
生產(chǎn)者Thread-3已生產(chǎn)完成汗侵,商品數(shù)量: 1
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量: 2
消費(fèi)者Thread-1已消費(fèi),剩余商品數(shù)量: 1
消費(fèi)者Thread-2已消費(fèi)晰韵,剩余商品數(shù)量: 0
生產(chǎn)者Thread-0已生產(chǎn)完成发乔,商品數(shù)量: 1
生產(chǎn)者Thread-3已生產(chǎn)完成,商品數(shù)量: 2
消費(fèi)者Thread-1已消費(fèi)雪猪,剩余商品數(shù)量: 1
消費(fèi)者Thread-2已消費(fèi)栏尚,剩余商品數(shù)量: 0
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量: 1
生產(chǎn)者Thread-3已生產(chǎn)完成浪蹂,商品數(shù)量: 2
消費(fèi)者Thread-2已消費(fèi)抵栈,剩余商品數(shù)量: 1
消費(fèi)者Thread-1已消費(fèi),剩余商品數(shù)量: 0
生產(chǎn)者Thread-3已生產(chǎn)完成坤次,商品數(shù)量: 1
生產(chǎn)者Thread-0已生產(chǎn)完成古劲,商品數(shù)量: 2
消費(fèi)者Thread-2已消費(fèi),剩余商品數(shù)量: 1
消費(fèi)者Thread-1已消費(fèi)缰猴,剩余商品數(shù)量: 0
方式3:(BlockingQueue)阻塞隊(duì)列 系列方法
示意圖
// 下面主要使用其中的 put()产艾、take()
// put():將指定元素插入此隊(duì)列中,將等待可用的空間(若有必要)
// take():獲取并移除此隊(duì)列的頭部滑绒,在指定的等待時(shí)間前等待可用的元素(若有必要)
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class Test {
private static Integer count = 0;
final BlockingQueue<Integer> bq = new ArrayBlockingQueue<Integer>(5);// 容量為5的阻塞隊(duì)列
public static void main(String[] args) {
Test t = new Test();
new Thread(t.new Producer()).start();
new Thread(t.new Consumer()).start();
new Thread(t.new Consumer()).start();
new Thread(t.new Producer()).start();
}
// 生產(chǎn)者
class Producer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
try {
bq.put(1);
count++;
System.out.println("生產(chǎn)者" + Thread.currentThread().getName()
+ "已生產(chǎn)完成闷堡,商品數(shù)量:" + count);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 消費(fèi)者
class Consumer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
try {
bq.take();
count--;
System.out.println("消費(fèi)者" + Thread.currentThread().getName()
+ "已消費(fèi),剩余商品數(shù)量:" + count);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
方式4:信號(hào)量 系列方法(Semaphore)
示意圖
// 實(shí)例使用
import java.util.concurrent.Semaphore;
public class Test {
int count = 0;
final Semaphore put = new Semaphore(5);// 初始令牌個(gè)數(shù)
// 注:同步令牌(notFull.acquire())必須在互斥令牌(mutex.acquire())前面獲得疑故;若先得到互斥鎖再發(fā)生等待杠览,會(huì)造成死鎖。
final Semaphore get = new Semaphore(0);
final Semaphore mutex = new Semaphore(1);
public static void main(String[] args) {
Test t = new Test();
new Thread(t.new Producer()).start();
new Thread(t.new Consumer()).start();
new Thread(t.new Consumer()).start();
new Thread(t.new Producer()).start();
}
class Producer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
try {
put.acquire();// 注意順序
mutex.acquire();
count++;
System.out.println("生產(chǎn)者" + Thread.currentThread().getName()
+ "已生產(chǎn)完成纵势,商品數(shù)量:" + count);
} catch (Exception e) {
e.printStackTrace();
} finally {
mutex.release();
get.release();
}
}
}
}
class Consumer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
try {
get.acquire();// 注意順序
mutex.acquire();
count--;
System.out.println("消費(fèi)者" + Thread.currentThread().getName()
+ "已消費(fèi)踱阿,剩余商品數(shù)量:" + count);
} catch (Exception e) {
e.printStackTrace();
} finally {
mutex.release();
put.release();
}
}
}
}
}
// 測試結(jié)果
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:1
消費(fèi)者Thread-2已消費(fèi)钦铁,剩余商品數(shù)量:0
生產(chǎn)者Thread-3已生產(chǎn)完成软舌,商品數(shù)量:1
消費(fèi)者Thread-1已消費(fèi),剩余商品數(shù)量:0
生產(chǎn)者Thread-0已生產(chǎn)完成牛曹,商品數(shù)量:1
生產(chǎn)者Thread-3已生產(chǎn)完成佛点,商品數(shù)量:2
消費(fèi)者Thread-2已消費(fèi),剩余商品數(shù)量:1
消費(fèi)者Thread-1已消費(fèi)黎比,剩余商品數(shù)量:0
生產(chǎn)者Thread-0已生產(chǎn)完成超营,商品數(shù)量:1
生產(chǎn)者Thread-3已生產(chǎn)完成,商品數(shù)量:2
消費(fèi)者Thread-2已消費(fèi)阅虫,剩余商品數(shù)量:1
消費(fèi)者Thread-1已消費(fèi)糟描,剩余商品數(shù)量:0
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:1
生產(chǎn)者Thread-3已生產(chǎn)完成书妻,商品數(shù)量:2
消費(fèi)者Thread-2已消費(fèi)船响,剩余商品數(shù)量:1
消費(fèi)者Thread-1已消費(fèi)躬拢,剩余商品數(shù)量:0
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:1
生產(chǎn)者Thread-3已生產(chǎn)完成见间,商品數(shù)量:2
消費(fèi)者Thread-2已消費(fèi)聊闯,剩余商品數(shù)量:1
消費(fèi)者Thread-1已消費(fèi),剩余商品數(shù)量:0
方式5:PipedInputStream / PipedOutputStream
示意圖
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
public class Test {
final PipedInputStream pis = new PipedInputStream();
final PipedOutputStream pos = new PipedOutputStream();
public static void main(String[] args) {
Test t = new Test();
new Thread(t.new Producer()).start();
new Thread(t.new Consumer()).start();
}
class Producer implements Runnable {
@Override
public void run() {
try {
pis.connect(pos);
} catch (IOException e) {
e.printStackTrace();
}
try {
while (true) { // 不斷的產(chǎn)生數(shù)據(jù)
int n = (int) (Math.random() * 255);
System.out.println("生產(chǎn)者" + Thread.currentThread().getName()
+ "已生產(chǎn)完成米诉,商品數(shù)量:" + n);
pos.write(n);
pos.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
pis.close();
pos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
@Override
public void run() {
int n;
try {
while (true) {
n = pis.read();
System.out.println("消費(fèi)者" + Thread.currentThread().getName()
+ "已消費(fèi)菱蔬,剩余商品數(shù)量:" + n);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
pis.close();
pos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
// 測試結(jié)果
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:6
生產(chǎn)者Thread-0已生產(chǎn)完成史侣,商品數(shù)量:158
生產(chǎn)者Thread-0已生產(chǎn)完成拴泌,商品數(shù)量:79
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:119
生產(chǎn)者Thread-0已生產(chǎn)完成惊橱,商品數(shù)量:93
生產(chǎn)者Thread-0已生產(chǎn)完成蚪腐,商品數(shù)量:213
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:151
生產(chǎn)者Thread-0已生產(chǎn)完成税朴,商品數(shù)量:101
生產(chǎn)者Thread-0已生產(chǎn)完成回季,商品數(shù)量:125
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:109
生產(chǎn)者Thread-0已生產(chǎn)完成正林,商品數(shù)量:67
生產(chǎn)者Thread-0已生產(chǎn)完成泡一,商品數(shù)量:109
生產(chǎn)者Thread-0已生產(chǎn)完成,商品數(shù)量:132
生產(chǎn)者Thread-0已生產(chǎn)完成觅廓,商品數(shù)量:139
...
至此鼻忠,關(guān)于Java解決生產(chǎn)者、消費(fèi)者問題的五種實(shí)現(xiàn)方式講解完畢杈绸。
歡迎關(guān)注Carson_Ho的簡書帖蔓!
分享Android技術(shù)干貨,追求短蝇棉、平讨阻、快芥永,但卻不缺深度篡殷。
請點(diǎn)贊!因?yàn)槟愕墓膭?lì)是我寫作的最大動(dòng)力埋涧!
相關(guān)文章閱讀
Android開發(fā):最全面板辽、最易懂的Android屏幕適配解決方案
Android事件分發(fā)機(jī)制詳解:史上最全面、最易懂
Android開發(fā):史上最全的Android消息推送解決方案
Android開發(fā):最全面棘催、最易懂的Webview詳解
Android開發(fā):JSON簡介及最全面解析方法!
Android四大組件:Service服務(wù)史上最全面解析
Android四大組件:BroadcastReceiver史上最全面解析