摘要:如果允許,則創(chuàng)建一個(gè)新的連接并返回給線程。然后在歸還連接成功之后,調(diào)用方法通知所有正在等待的線程可以繼續(xù)獲取連接了,但是繼續(xù)獲取連接時(shí),還必須繼續(xù)搶奪鎖,只有占鎖成功的線程,才能繼續(xù)執(zhí)行獲取連接操作。
前言
在了解到wait、notify的使用方式之后,我們使用wait、notify來實(shí)現(xiàn)一個(gè)連接池。如果還有不清楚wait、notify使用的,請進(jìn)入傳送門:https://segmentfault.com/a/1190000019104391
連接池原理首先我們先來了解下連接池的基本原理
線程調(diào)用連接池的方法來獲取連接
連接池內(nèi)部判斷連接池是否足夠,如果足夠則直接返回一個(gè)連接給線程
連接不夠的情況下,判斷連接池中是否允許新建連接(比如連接數(shù)小于最大連接數(shù))。如果允許,則創(chuàng)建一個(gè)新的連接并返回給線程。
如果不允許新建連接,則判斷是否允許等待空閑的連接,如果不允許,則未拿到連接
如果允許等待,之后就判斷等待是否超時(shí)。如果超時(shí),也是未拿到連接,如果未超時(shí),則繼續(xù)等待,一直循環(huán)。
代碼實(shí)現(xiàn)首先我們有一個(gè)空實(shí)現(xiàn)的連接類Connection
@Data static class Connection { private String connectionName; public Connection(String connectionName) { this.connectionName = connectionName; } }
此處只為了測試,該類只有一個(gè)名字屬性。另外使用了lombok來自動(dòng)生成set、get方法
接下來就是連接池的基本實(shí)現(xiàn)
@Data public class ConnectionPoolOfWaitNotify { private Integer capacity = 4;//連接池中的連接數(shù) LinkedListlinkedList = new LinkedList<>(); //連接容器 public ConnectionPoolOfWaitNotify() { IntStream.rangeClosed(1, capacity).forEach(i -> {//構(gòu)造方法中初始化所有連接 linkedList.addLast(new Connection("connection-" + i)); }); } //獲取連接 public Connection getConnectioin(long time) throws InterruptedException { synchronized (linkedList) { if (!linkedList.isEmpty()) {//如果存在,拿走第一個(gè) return linkedList.removeFirst(); } if (time <= 0) {//等到拿到連接再返回 while (linkedList.isEmpty()) { linkedList.wait(); } return linkedList.removeFirst(); } long lastTime = System.currentTimeMillis() + time; long sleepTime = time; while (linkedList.isEmpty() && sleepTime > 0) { linkedList.wait(sleepTime); sleepTime = lastTime - System.currentTimeMillis(); } if (!linkedList.isEmpty()) { return linkedList.removeFirst(); } else { return null; } } } //歸還連接 public void revertConnection(Connection connection) { synchronized (linkedList) { linkedList.addLast(connection); linkedList.notifyAll();//歸還連接后通知其他拿連接的線程 } } }
連接池中,主要有兩個(gè)方法。一個(gè)是獲取連接(可控制超時(shí)時(shí)間,超時(shí)時(shí)間小于等于0則永不超時(shí)),一個(gè)是歸還連接。
主要的邏輯在獲取連接的方法里面,當(dāng)獲取不到連接時(shí),使用wait()方法來使得當(dāng)前獲取連接的線程進(jìn)入等待狀態(tài)。然后在歸還連接成功之后,調(diào)用notifyAll()方法通知所有正在等待的線程可以繼續(xù)獲取連接了,但是繼續(xù)獲取連接時(shí),還必須繼續(xù)搶奪鎖,只有占鎖成功的線程,才能繼續(xù)執(zhí)行獲取連接操作。
之后執(zhí)行以下測試代碼
public static void main(String[] args) { int allNum = 100; AtomicInteger successNum = new AtomicInteger(0); ConnectionPoolOfWaitNotify connectionPoolOfWaitNotify = new ConnectionPoolOfWaitNotify(); IntStream.rangeClosed(1, allNum).parallel().forEach(i -> { Connection connection = null; try { connection = connectionPoolOfWaitNotify.getConnectioin(100); if (null != connection) { successNum.addAndGet(1); System.out.println("線程" + i + "拿到連接" + connection.getConnectionName()); Thread.sleep(200); } else { System.out.println("線程" + i + "沒有拿到連接"); } } catch (Exception e) { e.printStackTrace(); } finally { if (null != connection) { connectionPoolOfWaitNotify.revertConnection(connection); } } }); System.out.println("總共拿連接次數(shù):" + allNum + ",拿到連接次數(shù):" + successNum.get()); }
allNum為總共獲取連接次數(shù),successNum當(dāng)中記錄獲取成功的次數(shù)。每次拿連接的超時(shí)時(shí)間為100毫秒,拿到連接后200的休眠(模擬業(yè)務(wù)處理)之后歸還連接。而且在構(gòu)造函數(shù)中直接初始化了所有的連接(讀者可以考慮下如何做到按需來初始化的話)。運(yùn)行后結(jié)果如下
線程23拿到連接connection-1 線程69沒有拿到連接 線程10拿到連接connection-2 線程74拿到連接connection-4 線程25拿到連接connection-3 線程6沒有拿到連接 線程70沒有拿到連接 線程72沒有拿到連接 線程71沒有拿到連接 線程73沒有拿到連接 線程75拿到連接connection-1 總共拿連接次數(shù):100,拿到連接次數(shù):25
100次只能拿到25次,那我們?nèi)绻O(shè)置永不超時(shí)呢?調(diào)用方式如下,修改超時(shí)時(shí)間為0即可
connection = connectionPoolOfWaitNotify.getConnectioin(0);
之后再次運(yùn)行結(jié)果如下
線程70拿到連接connection-1 線程58拿到連接connection-2 線程16拿到連接connection-3 線程60拿到連接connection-4 線程71拿到連接connection-1 線程59拿到連接connection-4 線程67拿到連接connection-3 線程68拿到連接connection-2 總共拿連接次數(shù):100,拿到連接次數(shù):100
OK,全部拿到,沒毛病
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/74496.html
摘要:本文探討并發(fā)中的其它問題線程安全可見性活躍性等等。當(dāng)閉鎖到達(dá)結(jié)束狀態(tài)時(shí),門打開并允許所有線程通過。在從返回時(shí)被叫醒時(shí),線程被放入鎖池,與其他線程競爭重新獲得鎖。 本文探討Java并發(fā)中的其它問題:線程安全、可見性、活躍性等等。 在行文之前,我想先推薦以下兩份資料,質(zhì)量很高:極客學(xué)院-Java并發(fā)編程讀書筆記-《Java并發(fā)編程實(shí)戰(zhàn)》 線程安全 《Java并發(fā)編程實(shí)戰(zhàn)》中提到了太多的術(shù)語...
摘要:限期阻塞調(diào)用方法等待時(shí)間結(jié)束或線程執(zhí)行完畢。終止?fàn)顟B(tài)線程執(zhí)行完畢或出現(xiàn)異常退了。和都會(huì)檢查線程何時(shí)中斷,并且在發(fā)現(xiàn)中斷時(shí)提前放回。工廠方法將線程池的最大大小設(shè)置為,而將基本大小設(shè)置為,并將超時(shí)大小設(shè)置為分鐘。 wait()、notify()、notifyAll() Object是所有類的基類,它有5個(gè)方法組成了等待、通知機(jī)制的核心:notify()、notifyAll()、wait()...
摘要:可能會(huì)持有相同的值對象但鍵對象必須是唯一的。當(dāng)有新任務(wù)到達(dá)時(shí),線程池沒有線程則創(chuàng)建線程處理,處理完成后該線程緩存秒,過期后回收,線程過期前有新任務(wù)到達(dá)時(shí),則使用緩存的線程來處理。解決死鎖問題的三種方法預(yù)防死鎖檢測死鎖及避免死鎖。 最近辭職準(zhǔn)備面試,順便整理一下面試題分享給大家,如有錯(cuò)誤歡迎指出 01. 你對面向?qū)ο笏枷氲睦斫猓?面向?qū)ο缶幊毯喎QOOP,是開發(fā)程序的一種方法、思想。面向...
摘要:結(jié)構(gòu)型模式適配器模式橋接模式裝飾模式組合模式外觀模式享元模式代理模式。行為型模式模版方法模式命令模式迭代器模式觀察者模式中介者模式備忘錄模式解釋器模式模式狀態(tài)模式策略模式職責(zé)鏈模式責(zé)任鏈模式訪問者模式。 主要版本 更新時(shí)間 備注 v1.0 2015-08-01 首次發(fā)布 v1.1 2018-03-12 增加新技術(shù)知識、完善知識體系 v2.0 2019-02-19 結(jié)構(gòu)...
摘要:饑餓和公平一個(gè)線程因?yàn)闀r(shí)間全部被其他線程搶走而得不到運(yùn)行時(shí)間,這種狀態(tài)被稱之為饑餓。線程需要同時(shí)持有對象和對象的鎖,才能向線程發(fā)信號。現(xiàn)在兩個(gè)線程都檢查了這個(gè)條件為,然后它們都會(huì)繼續(xù)進(jìn)入第二個(gè)同步塊中并設(shè)置為。 1、死鎖 產(chǎn)生死鎖的四個(gè)必要條件:(1) 互斥條件:一個(gè)資源每次只能被一個(gè)進(jìn)程使用。(2) 請求與保持條件:一個(gè)進(jìn)程因請求資源而阻塞時(shí),對已獲得的資源保持不放。(3) 不剝奪條...
閱讀 1599·2021-11-02 14:48
閱讀 3651·2019-08-30 15:56
閱讀 2767·2019-08-30 15:53
閱讀 3208·2019-08-30 14:09
閱讀 3094·2019-08-30 12:59
閱讀 2853·2019-08-29 18:38
閱讀 2693·2019-08-26 11:41
閱讀 2209·2019-08-23 16:45