日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

Apache common-pool, common-dbcp源碼解讀與對

系統 2436 0

? 在項目中使用發送email的功能可能,多個客戶端要求發送email,為了不斷的減少對象的創建和銷毀,消耗內存,故采用對象池原理實現:發送email采用Commons-email,對象池采用common-pool創建一個對象池。

   各種池技術的應用連接池, 對象池技術, 將原有的未使用連接池的數據庫訪問操作改成連接池方式.性能有了非常大的提升, 事實證明, 經過兩次改造, 原來一個比較大的測試類需要500多秒, 第一次優化后只需要300多秒, 第二次改用連接池之后同一個測試類只需要80多秒.下面是改造過程中的一些總結.
對象池就是以”空間換時間”的 一種常用緩存機制, 這里的”時間”特指創建時間,因此這也給出了對象池的適用范圍:如果一種對象的創建過程非常耗時的話, 那么請使用對象池. 內部原理簡單的說, 就是將創建的對象放到一個容器中, 用完之后不是銷毀而是再放回該容器, 讓其他的對象調用, 對象池中還涉及到一些高級的技術, 比如過期銷毀, 被破壞時銷毀, 對象數超過池大小銷毀, 對象池中沒有可用空閑對象時等待等等.

apache的common-pool工具庫是對池化技術原理的一種具體實現. 在闡述原來之前, 這里先理解幾個概念:
對象池 (ObjectPool接口): 可以把它認為是一種容器, 它是用來裝池對象的, 并且包含了用來創建池對象的工廠對象
池對象 :就是要放到池容器中的對象, 理論上可以是任何對象.
對象池工廠 (ObjectPoolFactory接口):用來創建對象池的工廠, 這個沒什么好說的.
池對象工廠 (PoolableObjectFactory 接口):用來創建池對象, 將不用的池對象進行鈍化(passivateObject), 對要使用的池對象進行激活(activeObject), 對池對象進行驗證(validateObject), 對有問題的池對象進行銷毀(destroyObject)等工作

對象池中封裝了創建, 獲取, 歸還, 銷毀池對象的職責, 當然這些工作都是通過池對象工廠來實施的, 容器內部還有一個或多個用來盛池對象的容器.對象池會對容器大小, 存放時間, 訪問等待時間, 空閑時間等等進行一些控制, 因為可以根據需要來調整這些設置.

當 需要拿一個池對象的時候, 就從容器中取出一個, 如果容器中沒有的話, 而且又沒有達到容器的最大限制, 那么就調用池對象工廠, 新建一個池對象, 并調用工廠的激活方法, 對創建的對象進行激活, 驗證等一系列操作. 如果已經達到池容器的最大值, 而對象池中又經沒有空閑的對象, 那么將會繼續等待, 直到有新的空閑的對象被丟進來, 當然這個等待也是有限度的, 如果超出了這個限度, 對象池就會拋出異常.

“出來 混, 總是要還的”, 池對象也是如此, 當將用完的池對象歸還到對象池中的時候, 對象池會調用池對象工廠對該池對象進行驗證, 如果驗證不通過則被認為是有問題的對象, 將會被銷毀, 同樣如果容器已經滿了, 這個歸還池對象將變的”無家可歸”, 也會被銷毀, 如果不屬于上面兩種情況, 對象池就會調用工廠對象將其鈍化并放入容器中. 在整個過程中, 激活, 檢查, 鈍化處理都不是必須的, 因此我們在實現PoolableObjectFactory接口的時候, 一般不作處理, 給空實現即可, 所以誕生了BasePoolableObjectFactory.

當然你也可以將要已有的對象創建好, 然后通過addObject放到對象池中去, 以備后用.

為了確保對對象池的訪問都是線程安全的, 所有對容器的操作都必須放在synchronized中.

在 apache的common-pool工具庫中有5種對象池:GenericObjectPool和 GenericKeyedObjectPool, SoftReferenceObjectPool, StackObjectPool, StackKeyedObjectPool.
五種對象池可分為兩類, 一類是無key的:




另一類是有key的:

前面兩種用CursorableLinkedList來做容器, SoftReferenceObjectPool用ArrayList做容器, 一次性創建所有池化對象, 并對容器中的對象進行了軟引用(SoftReference)處理, 從而保證在內存充足的時候池對象不會輕易被jvm垃圾回收, 從而具有很強的緩存能力. 最后兩種用Stack做容器. 不帶key的對象池是對前面池技術原理的一種簡單實現, 帶key的相對復雜一些, 它會將池對象按照key來進行分類, 具有相同的key被劃分到一組類別中, 因此有多少個key, 就會有多少個容器. 之所以需要帶key的這種對象池, 是因為普通的對象池通過makeObject()方法創建的對象基本上都是一模一樣的, 因為沒法傳遞參數來對池對象進行定制. 因此四種池對象的區別主要體現在內部的容器的區別, Stack遵循”后進先出”的原則并能保證線程安全, CursorableLinkedList是一個內部用游標(cursor)來定位當前元素的雙向鏈表, 是非線程安全的, 但是能滿足對容器的并發修改.ArrayList是非線程安全的, 便利方便的容器.

使用對象池的一般步驟:創建一個池對象工廠, 將該工廠注入到對象池中, 當要取池對象, 調用borrowObject, 當要歸還池對象時, 調用returnObject, 銷毀池對象調用clear(), 如果要連池對象工廠也一起銷毀, 則調用close().
下面是一些時序圖:
borrowObject:

returnObject:

invalidateObject:

apache的連接池工具庫common-dbcp是common-pool在數據庫訪問方面的一個具體應用.當對common-pool熟悉之后, 對common-dbcp就很好理解了. 它通過對已有的Connection, Statment對象包裝成池對象PoolableConnection, PoolablePreparedStatement. 然后在這些池化的對象中, 持有一個對對象池的引用, 在關閉的時候, 不進行真正的關閉處理, 而是通過調用:
1. _pool.returnObject(this);
或:
1. _pool.returnObject(_key,this);
這樣一句, 將連接對象放回連接池中.
而對應的對象池前者采用的是ObjectPool, 后者是KeyedObjectPool, 因為一個數據庫只對應一個連接, 而執行操作的Statement卻根據Sql的不同會分很多種. 因此需要根據sql語句的不同多次進行緩存
在對連接池的管理上, common-dbcp主要采用兩種對象:
一個是PoolingDriver, 另一個是PoolingDataSource, 二者的區別是PoolingDriver是一個更底層的操作類, 它持有一個連接池映射列表, 一般針對在一個jvm中要連接多個數據庫, 而后者相對簡單一些. 內部只能持有一個連接池, 即一個數據源對應一個連接池.
下面是common-dbcp的結構關系:

下面是參考了common-dbcp的例子之后寫的一個從連接池中獲取連接的工具類

1. /**
2. * 創建連接
3. *
4. * @since 2009-1-22 下午02:58:35
5. */
6. public class ConnectionUtils {
7. // 一些common-dbcp內部定義的protocol
8. private static final String POOL_DRIVER_KEY = "jdbc:apache:commons:dbcp:";
9. private static final String POLLING_DRIVER = "org.apache.commons.dbcp.PoolingDriver";
10.
11. /**
12. * 取得池化驅動器
13. *
14. * @return
15. * @throws ClassNotFoundException
16. * @throws SQLException
17. */
18. private static PoolingDriver getPoolDriver() throws ClassNotFoundException,
19. SQLException {
20. Class.forName(POLLING_DRIVER);
21. return (PoolingDriver) DriverManager.getDriver(POOL_DRIVER_KEY);
22. }
23.
24. /**
25. * 銷毀所有連接
26. *
27. * @throws Exception
28. */
29. public static void destory() throws Exception {
30. PoolingDriver driver = getPoolDriver();
31. String[] names = driver.getPoolNames();
32. for (String name : names) {
33. driver.getConnectionPool(name).close();
34. }
35. }
36.
37. /**
38. * 從連接池中獲取數據庫連接
39. */
40. public static Connection getConnection(TableMetaData table)
41. throws Exception {
42. String key = table.getConnectionKey();
43.
44. PoolingDriver driver = getPoolDriver();
45.
46. ObjectPool pool = null;
47. // 這里找不到連接池會拋異常, 需要catch一下
48. try {
49. pool = driver.getConnectionPool(key);
50. } catch (Exception e) {
51. }
52.
53. if (pool == null) {
54. // 根據數據庫類型構建連接工廠
55. ConnectionFactory connectionFactory = null;
56. if (table.getDbAddr() != null
57. && TableMetaData.DB_TYPE_MYSQL == table.getDbType()) {
58. Class.forName(TableMetaData.MYSQL_DRIVER);
59. connectionFactory = new DriverManagerConnectionFactory(table
60. .getDBUrl(), null);
61. } else {
62. Class.forName(TableMetaData.ORACLE_DRIVER);
63. connectionFactory = new DriverManagerConnectionFactory(table
64. .getDBUrl(), table.getDbuser(), table.getDbpass());
65. }
66.
67. // 構造連接池
68. ObjectPool connectionPool = new GenericObjectPool(null);
69. new PoolableConnectionFactory(connectionFactory, connectionPool,
70. null, null, false, true);
71.
72. // 將連接池注冊到driver中
73. driver.registerPool(key, connectionPool);
74. }
75.
76. // 從連接池中拿一個連接
77. return DriverManager.getConnection(POOL_DRIVER_KEY + key);
78. }
79.
80. }

雖然對象池技術在實際開發過程中用的不是很多, 但是理解之后對我們寫程序還是有莫大的好處的, 至少我是這樣的

?

Apache common-pool, common-dbcp源碼解讀與對象池原理剖析


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!??!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 黄浦区| 双柏县| 江陵县| 台东县| 肇州县| 义乌市| 沛县| 汝阳县| 株洲县| 连南| 浮梁县| 阿拉尔市| 瓦房店市| 曲沃县| 德钦县| 思南县| 泗阳县| 成都市| 密山市| 茶陵县| 金塔县| 平顺县| 遂昌县| 墨竹工卡县| 彭水| 定州市| 长海县| 亳州市| 靖西县| 龙州县| 贵溪市| 鄱阳县| 交城县| 南开区| 湾仔区| 西畴县| 黄冈市| 浑源县| 贵德县| 周宁县| 井陉县|