自定義物件池實踐

FunTester發表於2024-03-27

自從研究了 commons-pool2 之後,進行了多次實踐,實現的效果也是非常好的。但是在一些輕量級場景當中,使用 commons-pool2 著實有點大材小用。

在某一次嘗試自定義的池化技術開發,最佳化服務記憶體的實踐當中,實在是忍無可忍,就動手自己寫了一個簡單的池化工具類。

思路

首先在簡單場景中,就是針對某一類物件,進行物件的快取。思路基本沿用了 commons-pool2 的設計思路。

  1. 使用佇列儲存快取物件
  2. 對外提供借出物件、歸還物件方法。
  3. 提供快取大小、控制快取數量 API,但不強制。

本次實踐以簡單為原則,已之前分享過的 Go 語言的物件池化中用到的 sync.Pool 工具類為基礎,使用到的物件參考使用 commons-pool2

程式碼

下面是我的第一版程式碼:

package com.funtester.funpool

import java.util.concurrent.LinkedBlockingQueue

/**
 * Object pool, using LinkedBlockingQueue to store objects, and using factory to create new objects, and using offer to return objects, and using poll to borrow objects, and using trimQueue to trim queue size
 * @param <F>
 */
class FunPool<F> {

    /**
     * Factory to create new objects
     */
    FunPooledFactory<F> factory

    /**
     * Object pool, using LinkedBlockingQueue to store objects
     */
    LinkedBlockingQueue<F> pool = new LinkedBlockingQueue<F>()

    FunPool(FunPooledFactory<F> factory) {
        this.factory = factory
    }

    /**
     * Borrow an object from the pool
     *
     * @param o the object to be borrowed
     * @return
     */
    F borrow() {
        F f = pool.poll()
        if (f == null) {
            f = factory.newInstance()
        }
        return f
    }

    /**
     * Return the object to the pool
     *
     * @param f the object to be returned
     */
    def back(F f) {
        boolean offer = pool.offer(f)
        if (!offer) f = null
    }

    /**
     * return size of object pool
     *
     * @return
     */
    int size() {
        return pool.size()
    }


    /**
     * Trim the queue size
     * @param size the size to be trimmed
     */
    def trimQueue(int size) {
        while (true) {
            if (size() <= size) break
            pool.poll()
        }
    }


}

下面是工廠類的介面:

package com.funtester.funpool  

/**  
 * Factory to create new objects * @param <F>  
 */interface FunPooledFactory<F> {  

    /**  
     * Create new objects     * @return  
     */  
    F newInstance()  

}

程式碼解讀:

這段程式碼實現了一個物件池(Object Pool)的核心功能,可以在需要時從池中獲取物件,並在不需要時將物件返回池中以供重複利用。讓我們更詳細地瞭解其中的一些關鍵點:

  1. 物件池設計思路: 物件池是一種常見的設計模式,旨在透過預先建立和快取物件來提高系統的效能和資源利用率。在高併發或頻繁建立銷燬物件的場景下,物件池可以顯著減少物件的建立和銷燬開銷。
  2. 工廠模式: 在這段程式碼中,使用了工廠模式來建立新的物件。透過 FunPooledFactory 介面及其實現類,可以靈活地建立不同型別的物件,並將其納入物件池的管理之中。
  3. 執行緒安全性: 使用了 LinkedBlockingQueue 作為物件池的儲存容器,這是一個執行緒安全的佇列實現。這意味著即使在多執行緒環境下,物件的借用和歸還操作也能夠保證執行緒安全。
  4. 物件借用與歸還: borrow() 方法用於從物件池中借用物件,它首先嚐試從佇列中取出一個物件,如果佇列為空,則透過工廠建立一個新物件,並返回。而 back() 方法則用於將物件歸還到物件池中,它嘗試將物件放入佇列中,如果佇列已滿,則丟棄該物件。
  5. 佇列大小控制: trimQueue() 方法用於調整佇列的大小,使其不超過指定的大小。這樣可以避免物件池佔用過多的記憶體資源,同時保證物件池的效能和效率。

這段程式碼實現了一個簡單但功能完備的物件池,可以用於管理和複用物件,提高系統的效能和資源利用率。

測試

測試指令碼如下:

static void main(String[] args) {  
    def pool = new FunPool<Demo>(new FunPooledFactory<Demo>() {//建立一個物件池  

        @Override  
        Demo newInstance() {//建立一個新的物件  
            return new Demo()  
        }  
    })  
    def first = pool.borrow()//從物件池中借一個物件  
    println first.hashCode()//列印物件的hashcode  
    2.times {//迴圈兩次  
        def borrow = pool.borrow()//從物件池中借一個物件  
        println borrow.hashCode()//列印物件的hashcode  
        pool.back(borrow)//將物件歸還物件池  
    }  
    output(pool.size())//列印物件池中的物件數量  

}  

/**  
 * 一個簡單的類  
 */  
static class Demo {  

}

控制檯列印:

1528769018
878991463
878991463
22:06:05:561 main 1
22:06:05 uptime:1 s

可以看出第一次借出和後兩次借出的物件非同一物件,而後兩次借出的是同一個物件。基本實現了一個物件池該有的功能。

  • 2021 年原創合集
  • 2022 年原創合集
  • 2023 年原創合集
  • 介面功能測試專題
  • 效能測試專題
  • Java、Groovy、Go、Python
  • 單元&白盒&工具合集
  • 測試方案&BUG&爬蟲&UI 自動化
  • 測試理論雞湯
  • 社群風采&影片合集
如果覺得我的文章對您有用,請隨意打賞。您的支援將鼓勵我繼續創作!
打賞支援
暫無回覆。

相關文章