Java編程生產(chǎn)者消費(fèi)者實(shí)現(xiàn)的四種方法
實(shí)現(xiàn)生產(chǎn)者消費(fèi)者的四種方式
一、最基礎(chǔ)的
利用 wait() 和 notify() 方法實(shí)現(xiàn),當(dāng)緩沖區(qū)滿或?yàn)榭諘r都調(diào)用 wait() 方法等待,當(dāng)生產(chǎn)者生產(chǎn)了一個產(chǎn)品或消費(fèi)者消費(fèi)了一個產(chǎn)品后會喚醒所有線程;
package com.practice; public class testMain { private static Integer count = 0; private static final Integer FULL = 10; private static String LOCK = "lock"; public static void main(String[] args) { testMain testMain = new testMain(); new Thread(testMain.new Producer()).start(); new Thread(testMain.new Consumer()).start(); new Thread(testMain.new Producer()).start(); new Thread(testMain.new Consumer()).start(); new Thread(testMain.new Producer()).start(); new Thread(testMain.new Consumer()).start(); new Thread(testMain.new Producer()).start(); new Thread(testMain.new Consumer()).start(); } class Producer implements Runnable{ @Override public void run(){ for (int i = 0; i < 10; i++) { try{ Thread.sleep(3000); }catch (Exception e){ e.printStackTrace(); } synchronized (LOCK){ while(count == FULL){//緩存空間滿了 try{ LOCK.wait();//線程阻塞 }catch (Exception e){ e.printStackTrace(); } } count++;//生產(chǎn)者 System.out.println(Thread.currentThread().getName() + "生產(chǎn)者生產(chǎn),目前總共有"+count); LOCK.notifyAll();//喚醒所有線程 } } } } class Consumer implements Runnable{ @Override public void run(){ for (int i = 0; i < 10; i++) { try{ Thread.sleep(3000); }catch (InterruptedException e){ e.printStackTrace(); } synchronized (LOCK){ while(count == 0){ try{ LOCK.wait(); }catch (Exception e){ } } count--; System.out.println(Thread.currentThread().getName() + "消費(fèi)者消費(fèi),目前總共有 "+count); LOCK.notifyAll();//喚醒所有線程 } } } } }
二、java.util.concurrent.lock 中的 Lock 框架
通過對 lock 的 lock() 方法和 unlock() 方法實(shí)現(xiàn)對鎖的顯示控制,而 synchronize()
則是對鎖的隱形控制,可重入鎖也叫做遞歸鎖,指的是同一個線程外層函數(shù)獲得鎖之后,內(nèi)層遞歸函數(shù)仍然有獲取該鎖的代碼,但不受影響;
簡單來說,該鎖維護(hù)這一個與獲取鎖相關(guān)的計(jì)數(shù)器,如果擁有鎖的某個線程再次得到鎖,那么獲計(jì)數(shù)器就加1,函數(shù)調(diào)用結(jié)束計(jì)數(shù)器就減1,然后鎖需要釋放兩次才能獲得真正釋放,已經(jīng)獲取鎖的線程進(jìn)入其他需要相同鎖的同步代碼塊不會被阻塞
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockTest { private static Integer count = 0; private static Integer FULL = 10; //創(chuàng)建一個鎖對象 private Lock lock = new ReentrantLock(); //創(chuàng)建兩個條件變量,一個為緩沖非滿,一個緩沖區(qū)非空 private final Condition notFull = lock.newCondition(); private final Condition notEmpty = lock.newCondition(); public static void main(String[] args){ ReentrantLockTest testMain = new ReentrantLockTest(); new Thread(testMain.new Producer()).start(); new Thread(testMain.new Consumer()).start(); new Thread(testMain.new Producer()).start(); new Thread(testMain.new Consumer()).start(); new Thread(testMain.new Producer()).start(); new Thread(testMain.new Consumer()).start(); new Thread(testMain.new Producer()).start(); new Thread(testMain.new Consumer()).start(); } class Producer implements Runnable{ @Override public void run(){ for (int i = 0; i <10; i++) { try { Thread.sleep(3000); } catch (Exception e) { e.printStackTrace(); } // 獲取鎖 lock.lock(); try { while (count == FULL) { try{ notFull.await(); }catch(InterruptedException e){ e.printStackTrace(); } } count++; System.out.println(Thread.currentThread().getName() + "生產(chǎn)者生產(chǎn),目前總共有" + count); }finally { lock.unlock(); } } } } class Consumer implements Runnable{ @Override public void run(){ for (int i = 0; i <10; i++) { try{ Thread.sleep(3000); } catch (Exception e){ e.printStackTrace(); } lock.lock(); try{ while(count==0){ try{ notEmpty.await(); }catch (InterruptedException e){ e.printStackTrace(); } } count--; System.out.println(Thread.currentThread().getName() + "消費(fèi)者消費(fèi),目前總共有 " + count); }finally { lock.unlock();//解鎖 } } } } }
三、阻塞隊(duì)列BlockingQueue的實(shí)現(xiàn)
被阻塞的情況主要分為如下兩種,BlockingQueue 是線程安全的
1,當(dāng)隊(duì)列滿了的時候進(jìn)行入隊(duì)操作;
2,當(dāng)隊(duì)列空的時候進(jìn)行出隊(duì)操作
Blockqueue 接口的一些方法
四類方法分別對應(yīng)于:
1,ThrowsException,如果操作不能馬上進(jìn)行,則拋出異常;
2,SpecialValue 如果操作不能馬上進(jìn)行,將會返回一個特殊的值,true或false;
3,Blocks 操作被阻塞;
4,TimeOut 指定時間未執(zhí)行返回一個特殊值 true 或 false
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; /** * 使用 BlockQueue 實(shí)現(xiàn)生產(chǎn)者消費(fèi)模型 */ public class BlockQueueTest { public static Integer count = 0; //創(chuàng)建一個阻塞隊(duì)列 final BlockingQueue blockingQueue = new ArrayBlockingQueue<>(10); public static void main(String[] args) { BlockQueueTest testMain = new BlockQueueTest(); new Thread(testMain.new Producer()).start(); new Thread(testMain.new Consumer()).start(); new Thread(testMain.new Producer()).start(); new Thread(testMain.new Consumer()).start(); new Thread(testMain.new Producer()).start(); new Thread(testMain.new Consumer()).start(); new Thread(testMain.new Producer()).start(); new Thread(testMain.new Consumer()).start(); } class Producer implements Runnable{ @Override public void run(){ for (int i = 0; i <10; i++) { try{ Thread.sleep(3000); }catch (Exception e){ e.printStackTrace(); } try{ blockingQueue.put(1); count++; System.out.println(Thread.currentThread().getName() + "生產(chǎn)者生產(chǎn),目前總共有 " + count); }catch (InterruptedException e){ e.printStackTrace(); } } } } class Consumer implements Runnable{ @Override public void run(){ for (int i = 0; i <10; i++) { try{ Thread.sleep(3000); }catch (InterruptedException e){ e.printStackTrace(); } try{ blockingQueue.take();//消費(fèi) count--; System.out.println(Thread.currentThread().getName() + " 消費(fèi)者消費(fèi),目前總共有 "+ count); }catch (InterruptedException e){ e.printStackTrace(); } } } } }
四、信號量 Semaphore 的實(shí)現(xiàn)
Semaphore (信號量) 用來控制同時訪問特定資源的線程數(shù)量,它通過協(xié)調(diào)各個線程,以保證合理的使用公共資源。Java中的 Semaphone 維護(hù)了一個許可集,一開始設(shè)定這個許可集的數(shù)量,使用 acquire()
方法獲得一個許可,當(dāng)許可不足時會被阻塞,release()
添加一個許可。
下面代碼中,還加入了 mutex
信號量,維護(hù)消費(fèi)者和生產(chǎn)者之間的同步關(guān)系,保證生產(chǎn)者消費(fèi)者之間的交替進(jìn)行
import java.util.concurrent.Semaphore; public class SemaphoreTest { private static Integer count = 0; //創(chuàng)建三個信號量 final Semaphore notFull = new Semaphore(10); final Semaphore notEmpty = new Semaphore(0); final Semaphore mutex = new Semaphore(1);//互斥鎖,控制共享數(shù)據(jù)的互斥訪問 public static void main(String[] args) { SemaphoreTest testMain = new SemaphoreTest(); new Thread(testMain.new Producer()).start(); new Thread(testMain.new Consumer()).start(); new Thread(testMain.new Producer()).start(); new Thread(testMain.new Consumer()).start(); new Thread(testMain.new Producer()).start(); new Thread(testMain.new Consumer()).start(); new Thread(testMain.new Producer()).start(); new Thread(testMain.new Consumer()).start(); } class Producer implements Runnable{ @Override public void run(){ for (int i = 0; i <10; i++) { try{ Thread.sleep(3000); }catch (InterruptedException e){ e.printStackTrace(); } try{ notFull.acquire();//獲取一個信號量 mutex.acquire(); count++; System.out.println(Thread.currentThread().getName() + "生產(chǎn)者生產(chǎn),目前總共有 "+count); } catch (InterruptedException e){ e.printStackTrace(); } finally { mutex.release();//添加 notEmpty.release(); } } } } class Consumer implements Runnable{ @Override public void run(){ for (int i = 0; i <10; i++) { try{ Thread.sleep(3000); }catch(InterruptedException e){ e.printStackTrace(); } try{ notEmpty.acquire(); mutex.acquire(); count--; System.out.println(Thread.currentThread().getName() + "消費(fèi)者消費(fèi),目前總共有"+count); }catch (InterruptedException e){ e.printStackTrace(); }finally { mutex.release(); notFull.release(); } } } } }
Reference
https://juejin.cn/post/6844903486895865864#comment
以上就是Java編程生產(chǎn)者消費(fèi)者實(shí)現(xiàn)的四種方法的詳細(xì)內(nèi)容,更多關(guān)于java實(shí)現(xiàn)生產(chǎn)消費(fèi)者的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
在Spring Boot中加載初始化數(shù)據(jù)的實(shí)現(xiàn)
這篇文章主要介紹了在Spring Boot中加載初始化數(shù)據(jù)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02JAVA中的Token 基于Token的身份驗(yàn)證實(shí)例
這篇文章主要介紹了JAVA中的Token 基于Token的身份驗(yàn)證實(shí)例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08Java語言面向?qū)ο缶幊趟枷胫惻c對象實(shí)例詳解
這篇文章主要介紹了Java語言面向?qū)ο缶幊趟枷胫惻c對象實(shí)例詳解,還是十分不錯的,這里給大家分享下,需要的朋友可以參考。2017-10-10Java(基于Struts2) 分頁實(shí)現(xiàn)代碼
這篇文章介紹了Java(基于Struts2) 分頁實(shí)現(xiàn)代碼,有需要的朋友可以參考一下2013-10-10Spring常用注解及自定義Filter的實(shí)現(xiàn)
這篇文章主要介紹了Spring常用注解及自定義Filter的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08SpringBoot整合Javamail實(shí)現(xiàn)郵件發(fā)送的詳細(xì)過程
日常開發(fā)過程中,我們經(jīng)常需要使用到郵件發(fā)送任務(wù),比方說驗(yàn)證碼的發(fā)送、日常信息的通知等,下面這篇文章主要給大家介紹了關(guān)于SpringBoot整合Javamail實(shí)現(xiàn)郵件發(fā)送的詳細(xì)過程,需要的朋友可以參考下2022-10-10Mybatis使用@one和@Many實(shí)現(xiàn)一對一及一對多關(guān)聯(lián)查詢
本文主要介紹了Mybatis使用@one和@Many實(shí)現(xiàn)一對一及一對多關(guān)聯(lián)查詢,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09Spring Boot Starters簡介及其優(yōu)劣勢
在這篇文章中,我們將向你介紹Spring Boot Starters,并將討論Spring Boot Starters的優(yōu)點(diǎn)和優(yōu)勢,感興趣的朋友跟隨腳本之家小編一起學(xué)習(xí)吧2018-05-05