├── .github └── FUNDING.yml ├── CHANGELOG ├── LICENSE ├── NOTICE ├── README.md ├── pom.xml └── src ├── main └── java │ └── org │ └── vibur │ └── objectpool │ ├── BasePool.java │ ├── ConcurrentPool.java │ ├── PoolObjectFactory.java │ ├── PoolService.java │ └── util │ ├── ArgumentValidation.java │ ├── ConcurrentCollection.java │ ├── ConcurrentLinkedDequeCollection.java │ ├── ConcurrentLinkedQueueCollection.java │ ├── ConcurrentStackCollection.java │ ├── Listener.java │ ├── MultithreadConcurrentQueueCollection.java │ ├── SamplingPoolReducer.java │ ├── TakenListener.java │ └── ThreadedPoolReducer.java └── test └── java └── org └── vibur └── objectpool ├── ConcurrentPoolTest.java ├── ExceptionThrowingObjectFactory.java ├── SimpleObjectFactory.java ├── perf └── ConcurrentPoolTestPerf.java └── util └── SamplingPoolReducerTest.java /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: simeonmalchev 2 | custom: [www.buymeacoffee.com/simeonmalchev] 3 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | Version 26.0 (09/01/2025) 2 | ========================= 3 | * Migrated the source code base to Java 10. 4 | * Updated all project dependencies to latest versions. 5 | * Removed support for OSGi, added Automatic-Module-Name for Java 9 modules. 6 | 7 | Version 25.0 (30/11/2019) 8 | ========================= 9 | * Allowed the ConcurrentPool ConcurrentCollection parameter to be a pre-initialized collection (or empty as before). 10 | 11 | Version 24.0 (28/11/2019) 12 | ========================= 13 | * Updated the disruptor dependency in pom.xml to true. 14 | 15 | Version 23.0 (15/05/2019) 16 | ========================= 17 | * Implemented catching and processing of undeclared checked exception in ConcurrentPool and SamplingPoolReducer. 18 | Such exceptions can be easily thrown from PoolObjectFactory if its implementation is in Kotlin. This processing 19 | logic is in addition to the existing logic for processing of RuntimeExceptions. 20 | 21 | Version 22.2 (05/05/2018) 22 | ========================= 23 | * Catching-up with Vibur DBCP version 22.2. 24 | 25 | Version 22.1 (30/03/2018) 26 | ========================= 27 | * Small refactoring in ConcurrentPool; moved the call to readyToRestore(..) to happen before the call to 28 | listener.onRestore(..). 29 | * Various javadoc improvements. 30 | 31 | Version 22.0 (08/02/2018) 32 | ========================= 33 | * Catching-up with Vibur DBCP version 22.0. 34 | 35 | Version 21.3 (28/01/2018) 36 | ========================= 37 | * Catching-up with Vibur DBCP version 21.3. 38 | 39 | Version 21.2 (29/11/2017) 40 | ========================= 41 | * Catching-up with Vibur DBCP version 21.2. 42 | 43 | Version 21.1 (27/11/2017) 44 | ========================= 45 | * Catching-up with Vibur DBCP version 21.1. 46 | 47 | Version 21.0 (22/11/2017) 48 | ========================= 49 | * Transitioned the SamplingPoolReducer time measuring to nanoseconds. 50 | 51 | Version 20.0 (26/09/2017) 52 | ========================= 53 | * Added a test for getting object from interrupted thread. 54 | 55 | Version 19.2 (14/09/2017) 56 | ========================= 57 | * Catching-up with Vibur DBCP version 19.2. 58 | 59 | Version 19.1 (17/08/2017) 60 | ========================= 61 | * Catching-up with Vibur DBCP version 19.1. 62 | 63 | Version 19.0 (09/08/2017) 64 | ========================= 65 | * Introduced 3 new methods in the PoolService interface that are counterparts of the existing take(), 66 | takeUninterruptibly(), and tryTake() methods respectively. The newly introduced methods allow 67 | the real time spent waiting for an object to become available in the pool to be reported back to the 68 | caller via the waitedNanos parameter. 69 | 70 | Version 18.0 (28/06/2017) 71 | ========================= 72 | * Catching-up with Vibur DBCP version 18.0. 73 | 74 | Version 17.1 (23/06/2017) 75 | ========================= 76 | * Changed the object handling flow on take/restore when the pool is terminated. 77 | 78 | Version 17.0 (05/04/2017) 79 | ========================= 80 | * Catching-up with Vibur DBCP version 17.0. 81 | 82 | Version 16.2 (10/02/2017) 83 | ========================= 84 | * Javadoc improvements. 85 | * Minor refactoring. 86 | 87 | Version 16.1 (11/01/2017) 88 | ========================= 89 | * Catching-up with Vibur DBCP version 16.1. 90 | 91 | Version 16.0 (09/01/2017) 92 | ========================= 93 | * Catching-up with Vibur DBCP version 16.0. 94 | 95 | Version 15.0 (04/01/2017) 96 | ========================= 97 | * Catching-up with Vibur DBCP version 15.0. 98 | 99 | Version 14.0 (22/12/2016) 100 | ========================= 101 | * Minor refactoring; catching-up with Vibur DBCP version 14.0. 102 | 103 | Version 13.0 (23/11/2016) 104 | ========================= 105 | * Added ConcurrentStackCollection as a stack pair of MultithreadConcurrentQueueCollection. 106 | 107 | Version 12.0 (11/11/2016) 108 | ========================= 109 | * Added Conversant Disruptor as an optional concurrent queue/collection used by ConcurrentPool. 110 | * Refactoring to accommodate third-party collections having interface different than java.util.Queue/Deque, 111 | particularly introduced the ConcurrentCollection adapter interface. 112 | To configure ConcurrentPool with Conversant Disruptor: 113 | pool = new ConcurrentPool<>(new MultithreadConcurrentQueueCollection<>(100), new ObjectFactory(), 1, 100, true); 114 | 115 | Version 11.0 (18/09/2016) 116 | ========================= 117 | * Minor change to the signature of getTaken() in TakenListener. 118 | 119 | Version 10.0 (31/07/2016) 120 | ========================= 121 | * Renamed reduceCreated() to reduceCreatedBy() and added reduceCreatedTo() method in the BasePool interface. 122 | * JavaDoc fixes, importantly, addressed https://github.com/vibur/vibur-object-pool/issues/3. 123 | 124 | Version 9.0 (02/05/2016) 125 | ======================== 126 | * Migrated the source code base to Java 1.7 and implemented AutoCloseable for ConcurrentLinkedPool - issue #1. 127 | * Fixed issue #2 - call destroy() on returned objects after pool termination; the implication of this fix is 128 | that now when closing a proxy connection after pool termination in the JDBC connection pool, the underlying 129 | physical connection will be closed, too. 130 | * Added a new parameter to the ConcurrentLinkedPool constructor in order to allow the underlying objects storing 131 | data structure used by the pool to be either FIFO or LIFO. Before it was always FIFO. 132 | * Refactoring - moved ThreadedPoolReducer and Listener interfaces and their implementation classes under 133 | org.vibur.objectpool.util package. 134 | 135 | Version 8.0 (02/02/2016) 136 | ======================== 137 | * Catching-up with Vibur DBCP version 8.0. 138 | 139 | Version 7.0 (10/01/2016) 140 | ======================== 141 | * Catching-up with Vibur DBCP version 7.0. 142 | 143 | Version 6.1 (05/01/2016) 144 | ======================== 145 | * Catching-up with Vibur DBCP version 6.1. 146 | 147 | Version 6.0 (19/12/2015) 148 | ========================== 149 | * The call to PoolObjectFactory.destroy() is now included in a try-finally block after calling 150 | PoolObjectFactory.readyToTake() or PoolObjectFactory.readyToRestore(). 151 | 152 | Version 5.0 (24/08/2015) 153 | ========================== 154 | * Catching-up with Vibur DBCP version 5.0. 155 | * Javadoc updates. 156 | 157 | Version 4.0 (08/08/2015) 158 | ========================== 159 | * Catching-up with Vibur DBCP version 4.0. 160 | * Various other refactoring and javadoc updates. 161 | 162 | Version 3.0 (03/06/2015) 163 | ========================== 164 | * Modified the SamplingPoolReducer pool shrinking algorithm to allow the number of allocated on the 165 | pool objects to reach zero. 166 | * Modified the SamplingPoolReducer logic to allow the afterReduce() method to be called even if the 167 | intended pool reduction is zero - useful for reporting/logging purposes. 168 | * Migrated the project to single digit version numbers, similarly to Vibur DBCP project. 169 | 170 | Version 2.0.0 (02/06/2014) 171 | ========================== 172 | * Overhauled the existing in version 1.0 validating and non-validating object pools and replaced 173 | them with a single object pool that does not provide any special validation means, but provides 174 | support for a Listener interface which methods will be called upon calling the pool take and 175 | restore operations. 176 | * Vibur Object Pool is now built and distributed as a valid R4 OSGi bundle. 177 | 178 | Version 1.0.0 (02/02/2014) 179 | ========================== 180 | * Making all vibur-dbcp dependencies having equal release version. 181 | 182 | Version 0.8.9 (15/10/2013) 183 | ========================== 184 | * Bug fix and refactoring. 185 | 186 | Version 0.8.8 (25/08/2013) 187 | ========================== 188 | * Minor refactoring and comments fixes. 189 | * The pool reducer functionality was re-implemented in the SamplingPoolReducer class. 190 | 191 | Version 0.8.7 (10/08/2013) 192 | ========================== 193 | * Packages structure refactoring. 194 | 195 | Version 0.8.6 (26/07/2013) 196 | ========================== 197 | * Minor refactoring, dependencies versions update. 198 | 199 | Version 0.8.5 (28/06/2013) 200 | ========================== 201 | * Fix in ConcurrentHolderLinkedPool, aiming to prevent the possibility that an object taken from 202 | one pool can be returned to another. 203 | * Changed the build artifact name to use "-" instead of "_". 204 | 205 | Version 0.8.4 (18/02/2013) 206 | ========================== 207 | * restore(Object, boolean valid) method introduced in the pool interfaces. 208 | * afterReduce(int, int, Throwable) method added in PoolReducer class. 209 | * Minor refactoring, improved javadoc, bug fix, 210 | lazily creates new objects if validates on restore fails. 211 | 212 | Version 0.8.3 (06/02/2013) 213 | ========================== 214 | * Minor refactoring, docs update. 215 | 216 | Version 0.8.2 (05/02/2013) 217 | ========================== 218 | * Major refactoring, have changed interface methods names, 219 | have added more information in the README file. 220 | 221 | Version 0.8.1 (31/01/2013) 222 | ========================== 223 | * Changed the reduceCreated(int) and drainCreated() contracts to allows the 224 | createdTotal() to become less than the initialSize(). I.e. a call to 225 | reduceCreated(int) may bring the number of created objects in the pool to zero. 226 | 227 | Version 0.8.0 (30/01/2013) 228 | ========================== 229 | * First tagged release. 230 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Vibur Object Pool 2 | A general-purpose concurrent Java object pool. 3 | Copyright 2013-2025 Simeon Malchev 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Vibur logo 2 | 3 | Vibur Object Pool is a general-purpose concurrent Java object pool that is built entirely using standard 4 | Java concurrency utilities, does not use any synchronized blocks or methods, and does not have any 5 | external dependencies. Vibur Object Pool has been around for more than 10 years now. 6 | 7 | The project [home page](http://www.vibur.org/vibur-object-pool/) contains details of its inner workings, 8 | usage examples, and more. 9 | 10 | This project is a main building block of [Vibur DBCP](https://github.com/vibur/vibur-dbcp) - a concurrent 11 | and dynamic JDBC connection pool. 12 | 13 | The project maven coordinates are: 14 | 15 | ``` 16 | 17 | org.vibur 18 | vibur-object-pool 19 | 26.0 20 | 21 | ``` 22 | 23 | If you like these Vibur utilities, please ⭐ them here on GitHub. If you're using them in your projects and if you'd like 24 | to support their future development and upkeep, you can buy-me-a-coffee at: 25 | 26 | [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/simeonmalchev) 27 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 4.0.0 20 | 21 | org.vibur 22 | vibur-object-pool 23 | 27.0-SNAPSHOT 24 | jar 25 | 26 | Vibur Object Pool 27 | 28 | General-purpose concurrent Java object pool that is built entirely using standard Java concurrency utilities, 29 | does not use any synchronized blocks or methods, and does not have any external dependencies. 30 | 31 | http://www.vibur.org/vibur-object-pool/ 32 | 2013 33 | 34 | 35 | 36 | The Apache Software License, Version 2.0 37 | http://www.apache.org/licenses/LICENSE-2.0.txt 38 | repo 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | scm:git:https://github.com/vibur/vibur-object-pool.git 50 | scm:git:https://github.com/vibur/vibur-object-pool.git 51 | https://github.com/vibur/vibur-object-pool/tree/master/ 52 | HEAD 53 | 54 | 55 | 56 | 57 | Simeon Malchev 58 | simeon.malchev@gmail.com 59 | simeonmalchev 60 | http://simeonmalchev.com 61 | +10 62 | 63 | owner 64 | developer 65 | 66 | 67 | 68 | 69 | 70 | vibur.org 71 | http://www.vibur.org/vibur-object-pool/ 72 | 73 | 74 | 75 | https://github.com 76 | https://github.com/vibur/vibur-object-pool/issues 77 | 78 | 79 | 80 | UTF-8 81 | UTF-8 82 | 83 | 10 84 | 1.2.21 85 | 5.11.4 86 | 87 | 3.13.0 88 | 3.1.1 89 | 3.11.2 90 | 3.4.2 91 | 3.3.1 92 | 93 | 94 | 95 | 98 | 99 | com.conversantmedia 100 | disruptor 101 | ${disruptor.version} 102 | true 103 | 104 | 105 | 106 | org.junit.jupiter 107 | junit-jupiter 108 | ${junit.jupiter.version} 109 | test 110 | 111 | 112 | 113 | 114 | 115 | 116 | org.apache.maven.plugins 117 | maven-compiler-plugin 118 | ${maven.compiler.plugin.version} 119 | 120 | ${java.version} 121 | 122 | 123 | 124 | 125 | org.apache.maven.plugins 126 | maven-release-plugin 127 | ${maven.release.plugin.version} 128 | 129 | 130 | org.apache.maven.scm 131 | maven-scm-provider-gitexe 132 | 2.1.0 133 | 134 | 135 | 136 | deploy 137 | 138 | 139 | 140 | 141 | org.apache.maven.plugins 142 | maven-javadoc-plugin 143 | ${maven.javadoc.plugin.version} 144 | 145 | none 146 | ${project.basedir}/target 147 | 148 | 149 | 150 | aggregate 151 | 152 | aggregate 153 | 154 | site 155 | 156 | 157 | 158 | 159 | 160 | org.apache.maven.plugins 161 | maven-jar-plugin 162 | ${maven.jar.plugin.version} 163 | 164 | 165 | 166 | org.vibur.objectpool 167 | 168 | 169 | 170 | 171 | 172 | 173 | test-jar 174 | 175 | 176 | 177 | 178 | 179 | 180 | org.apache.maven.plugins 181 | maven-source-plugin 182 | ${maven.source.plugin.version} 183 | 184 | 185 | attach-sources 186 | verify 187 | 188 | jar-no-fork 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /src/main/java/org/vibur/objectpool/BasePool.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool; 18 | 19 | /** 20 | * Defines the base object pool operations. These operations include various pool metrics, 21 | * pool termination methods, and support for shrinking (reduction) of the number of allocated 22 | * on the pool objects. 23 | * 24 | * @author Simeon Malchev 25 | */ 26 | public interface BasePool extends AutoCloseable { 27 | 28 | /** 29 | * Returns the number of objects taken from this object pool. 30 | * This number is less than or equal to the object pool {@link #createdTotal()}. 31 | * Typically used for testing and debugging purposes. 32 | * 33 | * @return the number of objects taken from this object pool 34 | */ 35 | int taken(); 36 | 37 | /** 38 | * Returns the number of remaining created objects which are currently available in this object pool. 39 | * This number is less than or equal to the object pool {@link #remainingCapacity()}. 40 | * Typically used for testing and debugging purposes. 41 | * 42 | * @return the number of remaining created objects in this object pool 43 | */ 44 | int remainingCreated(); 45 | 46 | /** 47 | * Returns the total number of created objects which currently exist for this object pool. 48 | * This number is equal to {@link #taken()} + {@link #remainingCreated()}. 49 | * Typically used for testing and debugging purposes. 50 | * 51 | * @return the total number of created objects for this object pool 52 | */ 53 | int createdTotal(); 54 | 55 | /** 56 | * Returns the remaining capacity of this object pool, i.e. the number of objects which could be 57 | * taken from this object pool without blocking. It is not guaranteed that all these objects 58 | * exist at the time of the call (i.e. are already created) in the object pool - some of them 59 | * might be created on demand upon take requests. Also see {@link #remainingCreated()}. 60 | * Typically used for testing and debugging purposes. 61 | * 62 | * @return the object pool remaining capacity 63 | */ 64 | int remainingCapacity(); 65 | 66 | /** 67 | * Returns the {@code initialSize} of this object pool at construction time. 68 | * This parameter never changes. 69 | * 70 | * @return the object pool {@code initialSize} 71 | */ 72 | int initialSize(); 73 | 74 | /** 75 | * Returns the {@code maxSize} of this object pool. This parameter never changes. 76 | * 77 | * @return the object pool {@code maxSize} 78 | */ 79 | int maxSize(); 80 | 81 | 82 | /** 83 | * Tries to remove (and destroy) up to {@code reduceBy} objects from the object pool. This method may 84 | * bring the object pool {@link #createdTotal()} to a number less than its {@link #initialSize()}. 85 | * 86 | * @param reduceBy the desired amount of objects to be removed 87 | * @param ignoreInitialSize specifies whether the {@link #createdTotal()} may be 88 | * reduced to less than {@link #initialSize()} 89 | * @return the actual amount of objects removed 90 | */ 91 | int reduceCreatedBy(int reduceBy, boolean ignoreInitialSize); 92 | 93 | /** 94 | * Tries to remove (and destroy) such number of objects from the object pool that the number of 95 | * {@link #createdTotal()} objects in the pool to become equal of {@code reduceTo}. This method may bring 96 | * the object pool {@link #createdTotal()} to a number smaller than its {@link #initialSize()}. 97 | * 98 | * @param reduceTo the desired amount of created objects to remain in the pool 99 | * @param ignoreInitialSize specifies whether the {@link #createdTotal()} may be 100 | * reduced to less than {@link #initialSize()} 101 | * @return the actual amount of objects removed 102 | */ 103 | int reduceCreatedTo(int reduceTo, boolean ignoreInitialSize); 104 | 105 | /** 106 | * Tries to remove (and destroy) as many created objects from this object pool as possible. 107 | * May bring the object pool {@link #createdTotal()} to a number less than its {@link #initialSize()}. 108 | * 109 | * @return the actual amount of objects removed (and destroyed) 110 | */ 111 | int drainCreated(); 112 | 113 | 114 | /** 115 | * Terminates this object pool. Once terminated the object pool cannot be more revived. 116 | * All take and restore operations called on a terminated object pool should throw 117 | * an exception or be ignored. This method can be invoked multiple times. 118 | */ 119 | void terminate(); 120 | 121 | /** 122 | * Returns the current terminated state of this object pool. 123 | * 124 | * @return {@code true} if the object pool is terminated; false otherwise 125 | */ 126 | boolean isTerminated(); 127 | 128 | /** 129 | * A synonym for {@link #terminate()}. Overrides the {@link AutoCloseable}'s method in order to overrule 130 | * the throwing of a checked {@code Exception}. 131 | */ 132 | @Override 133 | void close(); 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/org/vibur/objectpool/ConcurrentPool.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool; 18 | 19 | import org.vibur.objectpool.util.ConcurrentCollection; 20 | import org.vibur.objectpool.util.Listener; 21 | 22 | import java.util.concurrent.Semaphore; 23 | import java.util.concurrent.TimeUnit; 24 | import java.util.concurrent.atomic.AtomicBoolean; 25 | import java.util.concurrent.atomic.AtomicInteger; 26 | 27 | import static java.util.Objects.requireNonNull; 28 | import static org.vibur.objectpool.util.ArgumentValidation.forbidIllegalArgument; 29 | 30 | /** 31 | * An object pool based on a {@link ConcurrentCollection} guarded by a {@link Semaphore}. If the injected 32 | * in the pool {@code ConcurrentCollection} has native implementation for {@code offerFirst()} then 33 | * this pool will operate in LIFO mode, otherwise in FIFO mode. 34 | * 35 | *

This pool enforces a maximum limit on the number of objects that can be contained or taken out of it at 36 | * any time. The pool will lazily create an object upon {@link #tryTake take} request if no ready and valid object 37 | * exists in it at the time of the call; not all objects need to exist and be valid in the pool at all times. 38 | * The {@link #restore} methods do not provide any validation whether the currently restored object has been taken 39 | * before that from the pool or whether it is in taken state. Correct usage of the {@code restore} operations is 40 | * established by programming convention in the application. 41 | * 42 | *

The pool provides support for fairness with regard to the waiting takers threads. 43 | * The creation of new objects and their lifecycle are controlled by the supplied during the 44 | * pool creation time {@link PoolObjectFactory}. If a {@code Listener} instance has been 45 | * supplied when instantiating the pool, its methods will be called when the pool executes {@code take} 46 | * or {@code restore} operations. 47 | * 48 | *

This pool also provides support for shrinking (reduction) of the number of allocated in it objects. 49 | * Note that the shrinking may reduce the {@link #createdTotal()} to less than the pool {@link #initialSize()}. 50 | * 51 | *

The pool cannot contain {@code null} objects. 52 | * 53 | * @author Simeon Malchev 54 | * @param the type of objects held in the pool 55 | */ 56 | public class ConcurrentPool implements PoolService { 57 | 58 | private static final int RESERVED = 4096; 59 | private static final int MAX_ALLOWED_SIZE = Integer.MAX_VALUE - RESERVED; 60 | 61 | private final ConcurrentCollection available; 62 | private final Semaphore takeSemaphore; 63 | 64 | private final PoolObjectFactory poolObjectFactory; 65 | private final Listener listener; 66 | 67 | private final int initialSize; 68 | private final int maxSize; 69 | private final AtomicInteger createdTotal; 70 | 71 | private final AtomicBoolean terminated = new AtomicBoolean(false); 72 | 73 | /** 74 | * Creates a new {@code ConcurrentPool} with the given {@link PoolObjectFactory}, initial and max sizes, 75 | * and fairness setting. 76 | * 77 | * @param available the concurrent collection that will store the pooled objects; 78 | * it must be an empty collection or a collection pre-initialized with 79 | * {@code initialSize} objects 80 | * @param poolObjectFactory the factory which will be used to create new objects 81 | * in this object pool as well as to control their lifecycle 82 | * @param initialSize the object pool initial size, i.e. the initial number of 83 | * allocated in the object pool objects; this parameter never changes 84 | * @param maxSize the object pool max size, i.e. the max number of allocated 85 | * in the object pool objects; this parameter never changes 86 | * @param fair the object pool fairness setting with regard to waiting threads 87 | * @throws IllegalArgumentException if one of the following holds:
88 | * {@code initialSize < 0 || maxSize < 1 || maxSize < initialSize} 89 | * @throws NullPointerException if {@code available} or {@code poolObjectFactory} are null 90 | */ 91 | public ConcurrentPool(ConcurrentCollection available, PoolObjectFactory poolObjectFactory, 92 | int initialSize, int maxSize, boolean fair) { 93 | this(available, poolObjectFactory, initialSize, maxSize, fair, null); 94 | } 95 | 96 | /** 97 | * Creates a new {@code ConcurrentPool} with the given {@link PoolObjectFactory}, initial and max sizes, 98 | * and fairness setting. 99 | * 100 | * @param available the concurrent collection that will store the pooled objects; 101 | * it must be an empty collection or a collection pre-initialized with 102 | * {@code initialSize} objects 103 | * @param poolObjectFactory the factory which will be used to create new objects 104 | * in this object pool as well as to control their lifecycle 105 | * @param initialSize the object pool initial size, i.e. the initial number of 106 | * allocated in the object pool objects; this parameter never changes 107 | * @param maxSize the object pool max size, i.e. the max number of allocated 108 | * in the object pool objects; this parameter never changes 109 | * @param fair the object pool fairness setting with regard to waiting threads 110 | * @param listener if not {@code null}, this listener instance methods will be called 111 | * when the pool executes {@code take} or {@code restore} operations 112 | * @throws IllegalArgumentException if one of the following holds:
113 | * {@code initialSize < 0 || maxSize < 1 || maxSize < initialSize} 114 | * @throws NullPointerException if {@code available} or {@code poolObjectFactory} are null 115 | */ 116 | public ConcurrentPool(ConcurrentCollection available, PoolObjectFactory poolObjectFactory, 117 | int initialSize, int maxSize, boolean fair, Listener listener) { 118 | forbidIllegalArgument(initialSize < 0, "initialSize"); 119 | forbidIllegalArgument(maxSize < 1 || maxSize < initialSize || maxSize > MAX_ALLOWED_SIZE, "maxSize"); 120 | var availableSize = available.size(); // implicit null check of available collection 121 | forbidIllegalArgument(availableSize != 0 && availableSize != initialSize, "available"); 122 | 123 | this.available = available; 124 | this.poolObjectFactory = requireNonNull(poolObjectFactory); 125 | this.listener = listener; 126 | 127 | this.initialSize = initialSize; 128 | this.maxSize = maxSize; 129 | this.takeSemaphore = new Semaphore(maxSize, fair); 130 | 131 | this.createdTotal = new AtomicInteger(availableSize); 132 | if (availableSize == 0) { 133 | addInitialObjects(); 134 | } 135 | } 136 | 137 | private void addInitialObjects() { 138 | try { 139 | for (var i = 0; i < initialSize; i++) { 140 | available.offerLast(requireNonNull(poolObjectFactory.create())); 141 | createdTotal.incrementAndGet(); 142 | } 143 | } catch (Throwable t) { // equivalent to catching "RuntimeException | Error", however, better for Kotlin interoperability 144 | drainCreated(); 145 | throw t; 146 | } 147 | } 148 | 149 | @Override 150 | public T take() { 151 | try { 152 | takeSemaphore.acquire(); 153 | return takeObject(); 154 | } catch (InterruptedException ignored) { 155 | Thread.currentThread().interrupt(); // ignore and reset 156 | return null; 157 | } 158 | } 159 | 160 | @Override 161 | public T take(long[] waitedNanos) { 162 | try { 163 | var startTime = System.nanoTime(); 164 | try { 165 | takeSemaphore.acquire(); 166 | } finally { 167 | waitedNanos[0] = System.nanoTime() - startTime; 168 | } 169 | 170 | return takeObject(); 171 | } catch (InterruptedException ignored) { 172 | Thread.currentThread().interrupt(); // ignore and reset 173 | return null; 174 | } 175 | } 176 | 177 | @Override 178 | public T takeUninterruptibly() { 179 | takeSemaphore.acquireUninterruptibly(); 180 | return takeObject(); 181 | } 182 | 183 | @Override 184 | public T takeUninterruptibly(long[] waitedNanos) { 185 | var startTime = System.nanoTime(); 186 | try { 187 | takeSemaphore.acquireUninterruptibly(); 188 | } finally { 189 | waitedNanos[0] = System.nanoTime() - startTime; 190 | } 191 | 192 | return takeObject(); 193 | } 194 | 195 | @Override 196 | public T tryTake(long timeout, TimeUnit unit) { 197 | try { 198 | if (!takeSemaphore.tryAcquire(timeout, unit)) { 199 | return null; 200 | } 201 | return takeObject(); 202 | } catch (InterruptedException ignored) { 203 | Thread.currentThread().interrupt(); // ignore and reset 204 | return null; 205 | } 206 | } 207 | 208 | @Override 209 | public T tryTake(long timeout, TimeUnit unit, long[] waitedNanos) { 210 | try { 211 | var startTime = System.nanoTime(); 212 | try { 213 | if (!takeSemaphore.tryAcquire(timeout, unit)) { 214 | return null; 215 | } 216 | } finally { 217 | waitedNanos[0] = System.nanoTime() - startTime; 218 | } 219 | 220 | return takeObject(); 221 | } catch (InterruptedException ignored) { 222 | Thread.currentThread().interrupt(); // ignore and reset 223 | return null; 224 | } 225 | } 226 | 227 | @Override 228 | public T tryTake() { 229 | if (!takeSemaphore.tryAcquire()) { 230 | return null; 231 | } 232 | return takeObject(); 233 | } 234 | 235 | private T takeObject() { 236 | var object = prepareToTake(available.pollFirst()); 237 | 238 | if (listener != null) { 239 | listener.onTake(object); 240 | } 241 | 242 | if (isTerminated()) { 243 | restore(object, false); 244 | return null; 245 | } 246 | 247 | return object; 248 | } 249 | 250 | @Override 251 | public void restore(T object) { 252 | restore(object, true); 253 | } 254 | 255 | @Override 256 | public void restore(T object, boolean valid) { 257 | var ready = readyToRestore(requireNonNull(object), valid); 258 | 259 | if (listener != null) { 260 | listener.onRestore(object, valid); 261 | } 262 | 263 | if (ready) { 264 | available.offerFirst(object); 265 | } 266 | takeSemaphore.release(); 267 | 268 | if (isTerminated() && valid) { 269 | terminate(); 270 | } 271 | } 272 | 273 | /** 274 | * Verifies whether the given object is valid and whether it can be given to the calling application. 275 | * If the object is {@code null}, a new object will be created and returned. If the object 276 | * is not {@code null}, it will be validated and if the validation fails, a new object will be created 277 | * and returned; otherwise a reference to the object itself will be returned. This method never 278 | * returns {@code null}. 279 | * 280 | * @param object the object to be validated 281 | * @return see above 282 | */ 283 | private T prepareToTake(T object) { 284 | try { 285 | if (object == null) { 286 | createdTotal.incrementAndGet(); 287 | object = requireNonNull(poolObjectFactory.create()); 288 | } 289 | else { 290 | var ready = false; 291 | try { 292 | ready = poolObjectFactory.readyToTake(object); 293 | } finally { 294 | if (!ready) { 295 | poolObjectFactory.destroy(object); 296 | } 297 | } 298 | if (!ready) { 299 | object = requireNonNull(poolObjectFactory.create()); 300 | } 301 | } 302 | 303 | return object; 304 | } catch (Throwable t) { // equivalent to catching "RuntimeException | Error", however, better for Kotlin interoperability 305 | recoverInnerState(); 306 | throw t; 307 | } 308 | } 309 | 310 | /** 311 | * Verifies whether the given object is valid and whether it can be restored back to the pool. Returns {@code true} 312 | * if the object is valid; otherwise returns {@code false}. 313 | * 314 | * @param object the object that is to be restored to the pool and that needs to be validated 315 | * @param valid if {@code false}, the object is treated as invalid; otherwise a secondary validation on the object 316 | * will be performed 317 | * @return see above 318 | */ 319 | private boolean readyToRestore(T object, boolean valid) { 320 | try { 321 | var ready = false; 322 | try { 323 | ready = valid && poolObjectFactory.readyToRestore(object); 324 | } finally { 325 | if (!ready) { 326 | poolObjectFactory.destroy(object); 327 | } 328 | } 329 | if (!ready) { 330 | createdTotal.decrementAndGet(); 331 | } 332 | 333 | return ready; 334 | } catch (Throwable t) { // equivalent to catching "RuntimeException | Error", however, better for Kotlin interoperability 335 | recoverInnerState(); 336 | throw t; 337 | } 338 | } 339 | 340 | private void recoverInnerState() { 341 | createdTotal.decrementAndGet(); 342 | takeSemaphore.release(); 343 | } 344 | 345 | 346 | @Override 347 | public Listener listener() { 348 | return listener; 349 | } 350 | 351 | @Override 352 | public int taken() { 353 | return !isTerminated() ? calculateTaken() : createdTotal(); 354 | } 355 | 356 | private int calculateTaken() { 357 | return maxSize() - remainingCapacity(); 358 | } 359 | 360 | @Override 361 | public int remainingCreated() { 362 | return !isTerminated() ? createdTotal() - calculateTaken() : 0; 363 | } 364 | 365 | @Override 366 | public int drainCreated() { 367 | return reduceCreatedTo(0, true); 368 | } 369 | 370 | @Override 371 | public String toString() { 372 | return super.toString() + (!isTerminated() ? "[remainingCreated = " + remainingCreated() + "]" : "[terminated]"); 373 | } 374 | 375 | 376 | @Override 377 | public int createdTotal() { 378 | return createdTotal.get(); 379 | } 380 | 381 | @Override 382 | public int remainingCapacity() { 383 | return !isTerminated() ? takeSemaphore.availablePermits() : 0; 384 | } 385 | 386 | @Override 387 | public int initialSize() { 388 | return initialSize; 389 | } 390 | 391 | @Override 392 | public int maxSize() { 393 | return maxSize; 394 | } 395 | 396 | 397 | @Override 398 | public int reduceCreatedBy(int reduceBy, boolean ignoreInitialSize) { 399 | forbidIllegalArgument(reduceBy < 0, "reduceBy"); 400 | 401 | for (var cnt = 0; cnt < reduceBy; cnt++) { 402 | if (!reduceByOne(ignoreInitialSize)) { 403 | return cnt; 404 | } 405 | } 406 | return reduceBy; 407 | } 408 | 409 | @Override 410 | public int reduceCreatedTo(int reduceTo, boolean ignoreInitialSize) { 411 | forbidIllegalArgument(reduceTo < 0, "reduceTo"); 412 | 413 | int cnt; 414 | for (cnt = 0; createdTotal() > reduceTo; cnt++) { 415 | if (!reduceByOne(ignoreInitialSize)) { 416 | break; 417 | } 418 | } 419 | return cnt; 420 | } 421 | 422 | private boolean reduceByOne(boolean ignoreInitialSize) { 423 | var newTotal = createdTotal.decrementAndGet(); 424 | if (!ignoreInitialSize && newTotal < initialSize) { 425 | createdTotal.incrementAndGet(); 426 | return false; 427 | } 428 | var object = available.pollLast(); 429 | if (object == null) { 430 | createdTotal.incrementAndGet(); 431 | return false; 432 | } 433 | poolObjectFactory.destroy(object); 434 | return true; 435 | } 436 | 437 | 438 | @Override 439 | public void terminate() { 440 | var wasTerminated = terminated.getAndSet(true); 441 | 442 | drainCreated(); 443 | 444 | if (!wasTerminated) { 445 | takeSemaphore.release(takeSemaphore.getQueueLength() + RESERVED); // best effort to unblock any waiting on the takeSemaphore threads 446 | } 447 | } 448 | 449 | @Override 450 | public void close() { 451 | terminate(); 452 | } 453 | 454 | @Override 455 | public boolean isTerminated() { 456 | return terminated.get(); 457 | } 458 | 459 | 460 | @Override 461 | public boolean isFair() { 462 | return takeSemaphore.isFair(); 463 | } 464 | } 465 | -------------------------------------------------------------------------------- /src/main/java/org/vibur/objectpool/PoolObjectFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool; 18 | 19 | /** 20 | * Defines the interface that will be implemented by the factory used by the object pools 21 | * defined in the package, in order to control the lifecycle of the objects in the pools. 22 | * 23 | * @author Simeon Malchev 24 | * @param the type of objects held in the client object pool 25 | */ 26 | public interface PoolObjectFactory { 27 | 28 | /** 29 | * Creates a new object for the calling object pool. This object is presumed to be ready (and valid) 30 | * for immediate use. Should never return {@code null}. 31 | * 32 | *

This method will be called by the constructors of {@link ConcurrentPool}, and by any of its 33 | * {@code take...} methods if they were able to obtain a permit from the counting {@code Semaphore} 34 | * guarding the pool, but there was no ready and valid object in the pool. I.e., this is the case when 35 | * a new object is created lazily in the pool upon request. 36 | * 37 | * @return a new object for this object pool 38 | */ 39 | T create(); 40 | 41 | /** 42 | * A validation/activation hook which will be called by the {@code take...} methods of 43 | * {@link ConcurrentPool} when an object from the object pool is requested by the application. 44 | * This is an optional operation which concrete implementation may simply always return {@code true}. 45 | * 46 | *

If there is a particular activation or validation which needs to be done 47 | * for the taken from the pool object, this is the ideal place where it can be done. 48 | * 49 | * @see #readyToRestore 50 | * 51 | * @param obj an object which is taken from the object pool and which is to be given 52 | * to the calling application 53 | * @return {@code true} if the validation/activation is successful, {@code false} otherwise 54 | */ 55 | boolean readyToTake(T obj); 56 | 57 | /** 58 | * A validation/passivation hook which will be called by the {@code restore} methods of 59 | * {@link ConcurrentPool} when an object taken before that from the object pool is about to be 60 | * restored (returned back) to the pool. This is an optional operation which concrete implementation 61 | * may simply always return {@code true}. 62 | * 63 | *

If there is a particular passivation or validation which needs to be done 64 | * for the restored to the pool object, this is the ideal place where it can be done. 65 | * 66 | * @see #readyToTake 67 | * 68 | * @param obj an object which has been taken before that from this object pool and which is now 69 | * to be restored to the pool 70 | * @return {@code true} if the validation/passivation is successful, {@code false} otherwise 71 | */ 72 | boolean readyToRestore(T obj); 73 | 74 | /** 75 | * A method which will be called when an object from the object pool needs to be destroyed, 76 | * which is when the {@link #readyToTake} or {@link #readyToRestore} methods have returned 77 | * {@code false}, or when the pool is shrinking its size (via calling {@code reduceCreatedBy/To}), 78 | * or when the pool is terminating. The simplest implementation of this method may simply 79 | * do nothing, however if there are any allocated resources associated with the to-be-destroyed 80 | * object, like network connections or similar, this is the ideal place where they can be 81 | * de-allocated. 82 | * 83 | * @param obj an object from the pool which needs to be destroyed 84 | */ 85 | void destroy(T obj); 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/org/vibur/objectpool/PoolService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool; 18 | 19 | import org.vibur.objectpool.util.Listener; 20 | 21 | import java.util.concurrent.TimeUnit; 22 | 23 | /** 24 | * Defines the object pool operations. These operations include the {@code take} and {@code restore} pool 25 | * methods. 26 | * 27 | *

This pool enforces a maximum limit on the number of objects that can be contained or taken out 28 | * of it at any time. The pool may lazily create an object upon {@code take} request; not all objects need 29 | * to exist and be valid in the pool at all times. The {@code restore} methods do not provide any validation 30 | * whether the currently restored object has been taken before that from the pool or whether it is in taken state. 31 | * Correct usage of the {@code restore} operations is established by programming convention in the application. 32 | * 33 | *

The object pool implementation may support an optional fairness parameter (usually provided via the 34 | * pool constructor) that defines the pool behaviour with regard to waiting takers threads, as well as 35 | * an optional {@code Listener} interface which methods will be called when a {@code take} or 36 | * {@code restore} pool method executes. 37 | * 38 | *

The pool cannot contain {@code null} objects. 39 | * 40 | * @author Simeon Malchev 41 | * @param the type of objects held in this object pool 42 | */ 43 | public interface PoolService extends BasePool { 44 | 45 | /** 46 | * A counterpart of {@link #take(long[])} that does not report back the time waited 47 | * to obtain an object from the pool. 48 | * 49 | * @return an object taken from the object pool or {@code null} if was interrupted while waiting 50 | */ 51 | T take(); 52 | 53 | /** 54 | * Takes an object from the object pool if there is such available. This is a blocking call that 55 | * waits indefinitely until an object becomes available; the object may need to be created as 56 | * described in {@link #tryTake(long, TimeUnit, long[])}. If the calling thread is interrupted 57 | * while waiting this call will return {@code null} and the thread's interrupted status will 58 | * be set to {@code true}. 59 | * 60 | * @param waitedNanos used to report the time waited, see {@link #tryTake(long, TimeUnit, long[])} 61 | * @return an object taken from the object pool or {@code null} if it was interrupted while waiting 62 | */ 63 | T take(long[] waitedNanos); 64 | 65 | /** 66 | * A counterpart of {@link #takeUninterruptibly(long[])} that does not report back the time waited 67 | * to obtain an object from the pool. 68 | * 69 | * @return an object taken from the object pool 70 | */ 71 | T takeUninterruptibly(); 72 | 73 | /** 74 | * Takes an object from the object pool if there is such available. This is a blocking call that 75 | * waits indefinitely until an object becomes available; the object may need to be created as 76 | * described in {@link #tryTake(long, TimeUnit, long[])}. 77 | * 78 | * @param waitedNanos used to report the time waited, see {@link #tryTake(long, TimeUnit, long[])} 79 | * @return an object taken from the object pool 80 | */ 81 | T takeUninterruptibly(long[] waitedNanos); 82 | 83 | /** 84 | * A counterpart of {@link #tryTake(long, TimeUnit, long[])} that does not report back the time waited 85 | * to obtain an object from the pool. 86 | * 87 | * @param timeout the maximum time to wait for an object to become available in the object pool; 88 | * this timeout does not include the object creation time, see above 89 | * @param unit the time unit of the {@code timeout} argument 90 | * @return an object taken from the object pool or {@code null} if the specified timeout expires 91 | * or if it was interrupted while waiting 92 | */ 93 | T tryTake(long timeout, TimeUnit unit); 94 | 95 | /** 96 | * Tries to take an object from the object pool if there is one available. This is a blocking call that 97 | * waits for an object to become available up to the specified {@code timeout}. The real time spent waiting is 98 | * reported back via the {@code waitedNanos} parameter. The total method execution time may also include the 99 | * object creation time - an object can be (lazily) created in the pool when the pool capacity is not reached 100 | * yet but no ready and valid object existed in the pool. If the calling thread is interrupted while waiting 101 | * this call will return {@code null} and the thread's interrupted status will be set to {@code true}. 102 | * 103 | * @param timeout the maximum time to wait for an object to become available in the object pool; 104 | * this timeout does not include the object creation time 105 | * @param unit the time unit of the {@code timeout} argument 106 | * @param waitedNanos this parameter is used to report the nanoseconds time waited for an object to become 107 | * available in the pool, excluding any object creation time; the time waited will be stored 108 | * at index {@code 0} of this array; the array must be of size of at least one 109 | * @return an object taken from the object pool or {@code null} if the specified timeout expires 110 | * or if it was interrupted while waiting 111 | */ 112 | T tryTake(long timeout, TimeUnit unit, long[] waitedNanos); 113 | 114 | /** 115 | * Tries to take an object from the object pool if there is one that is immediately available; the object may 116 | * need to be created as described in {@link #tryTake(long, TimeUnit, long[])}. Returns {@code null} if no object 117 | * is available in the pool at the time of the call. 118 | * 119 | * @return an object from the object pool or {@code null} if no object was available 120 | */ 121 | T tryTake(); 122 | 123 | /** 124 | * Restores (returns) an object to the object pool. The object pool does not 125 | * verify whether the currently restored object has been taken before that from this object pool 126 | * or whether it is currently in taken state. Equivalent to calling {@link #restore(Object, boolean) 127 | * restore(object, true)}. 128 | * 129 | * @param object the object to be restored / returned 130 | * @throws NullPointerException if the given object is {@code null} 131 | */ 132 | void restore(T object); 133 | 134 | /** 135 | * Restores (returns) an object to the object pool. The object pool does not 136 | * verify whether the currently restored object has been taken before that from this object pool 137 | * or whether it is currently in taken state. 138 | * 139 | * @param object the object to be restored / returned 140 | * @param valid if {@code false}, the object is treated as invalid; otherwise a secondary validation on the object 141 | * will be performed 142 | * @throws NullPointerException if the given object is {@code null} 143 | */ 144 | void restore(T object, boolean valid); 145 | 146 | 147 | /** 148 | * Returns the {@link Listener} interface instance associated with this object pool, if any. 149 | * 150 | * @return see above; {@code null} means no {@code Listener} is associated with this object pool. 151 | */ 152 | Listener listener(); 153 | 154 | 155 | /** 156 | * Returns the fairness setting of this object pool. 157 | * 158 | * @return {@code true} if the object pool is fair to waiting taker threads 159 | */ 160 | boolean isFair(); 161 | } 162 | -------------------------------------------------------------------------------- /src/main/java/org/vibur/objectpool/util/ArgumentValidation.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool.util; 18 | 19 | /** 20 | * @author Simeon Malchev 21 | */ 22 | public final class ArgumentValidation { 23 | 24 | private ArgumentValidation() { } 25 | 26 | public static void forbidIllegalArgument(boolean condition) { 27 | if (condition) { 28 | throw new IllegalArgumentException(); 29 | } 30 | } 31 | 32 | public static void forbidIllegalArgument(boolean condition, String param) { 33 | if (condition) { 34 | throw new IllegalArgumentException("Illegal value for parameter: \"" + param + "\""); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/vibur/objectpool/util/ConcurrentCollection.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool.util; 18 | 19 | /** 20 | * An adapter interface to a concurrent collection that provides 4 access methods: {@link #offerFirst}, 21 | * {@link #offerLast}, {@link #pollFirst}, and {@link #pollLast}. If the implementing subclass delegates the above 22 | * methods to a Collection that does not have native implementation for {@code offerFirst()} or {@code pollLast()} 23 | * it can implement these methods in the same way as {@code offerLast()} or {@code pollFirst()}, and the vice versa. 24 | * 25 | * @author Simeon Malchev 26 | * @param the type of objects held in this {@code ConcurrentCollection} 27 | */ 28 | public interface ConcurrentCollection { 29 | 30 | /** 31 | * Adds the given {@code object} at the head of the {@code ConcurrentCollection}. 32 | * 33 | * @param object the given object 34 | */ 35 | void offerFirst(T object); 36 | 37 | /** 38 | * Adds the given {@code object} at the tail of the {@code ConcurrentCollection}. 39 | * 40 | * @param object the given object 41 | */ 42 | void offerLast(T object); 43 | 44 | /** 45 | * Polls an {@code object} from the head of the {@code ConcurrentCollection}. 46 | * 47 | * @return the head Collection object if available; {@code null} otherwise 48 | */ 49 | T pollFirst(); 50 | 51 | /** 52 | * Polls an {@code object} from the tail of the {@code ConcurrentCollection}. 53 | * 54 | * @return the tail Collection object if available; {@code null} otherwise 55 | */ 56 | T pollLast(); 57 | 58 | /** 59 | * Returns the number of elements in this collection. 60 | * 61 | * @return the number of elements in this collection 62 | */ 63 | int size(); 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/org/vibur/objectpool/util/ConcurrentLinkedDequeCollection.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool.util; 18 | 19 | import java.util.Deque; 20 | import java.util.concurrent.ConcurrentLinkedDeque; 21 | 22 | /** 23 | * A {@link ConcurrentLinkedDeque} based implementation of {@link ConcurrentCollection}. 24 | * 25 | * @author Simeon Malchev 26 | * @param the type of objects held in this {@code ConcurrentCollection} 27 | */ 28 | public class ConcurrentLinkedDequeCollection implements ConcurrentCollection { 29 | 30 | private final Deque deque = new ConcurrentLinkedDeque<>(); 31 | 32 | @Override 33 | public void offerFirst(T object) { 34 | deque.addFirst(object); 35 | } 36 | 37 | @Override 38 | public void offerLast(T object) { 39 | deque.add(object); 40 | } 41 | 42 | @Override 43 | public T pollFirst() { 44 | return deque.poll(); 45 | } 46 | 47 | @Override 48 | public T pollLast() { 49 | return deque.pollLast(); 50 | } 51 | 52 | @Override 53 | public int size() { 54 | return deque.size(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/vibur/objectpool/util/ConcurrentLinkedQueueCollection.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool.util; 18 | 19 | import java.util.Queue; 20 | import java.util.concurrent.ConcurrentLinkedQueue; 21 | 22 | /** 23 | * A {@link ConcurrentLinkedQueue} based implementation of {@link ConcurrentCollection}. 24 | * 25 | * @author Simeon Malchev 26 | * @param the type of objects held in this {@code ConcurrentCollection} 27 | */ 28 | public class ConcurrentLinkedQueueCollection implements ConcurrentCollection { 29 | 30 | private final Queue queue = new ConcurrentLinkedQueue<>(); 31 | 32 | @Override 33 | public void offerFirst(T object) { 34 | offerLast(object); 35 | } 36 | 37 | @Override 38 | public void offerLast(T object) { 39 | queue.add(object); 40 | } 41 | 42 | @Override 43 | public T pollFirst() { 44 | return queue.poll(); 45 | } 46 | 47 | @Override 48 | public T pollLast() { 49 | return pollFirst(); 50 | } 51 | 52 | @Override 53 | public int size() { 54 | return queue.size(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/vibur/objectpool/util/ConcurrentStackCollection.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool.util; 18 | 19 | import com.conversantmedia.util.collection.Stack; 20 | import com.conversantmedia.util.concurrent.ConcurrentStack; 21 | 22 | 23 | /** 24 | * A {@link ConcurrentStack} based implementation of {@link ConcurrentCollection}. 25 | * 26 | * @author Simeon Malchev 27 | * @param the type of objects held in this {@code ConcurrentCollection} 28 | */ 29 | public class ConcurrentStackCollection implements ConcurrentCollection { 30 | 31 | private final Stack stack; 32 | 33 | public ConcurrentStackCollection(int capacity) { 34 | stack = new ConcurrentStack<>(capacity); 35 | } 36 | 37 | @Override 38 | public void offerFirst(T object) { 39 | stack.push(object); 40 | } 41 | 42 | @Override 43 | public void offerLast(T object) { 44 | offerFirst(object); 45 | } 46 | 47 | @Override 48 | public T pollFirst() { 49 | return stack.pop(); 50 | } 51 | 52 | @Override 53 | public T pollLast() { 54 | return pollFirst(); 55 | } 56 | 57 | @Override 58 | public int size() { 59 | return stack.size(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/vibur/objectpool/util/Listener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool.util; 18 | 19 | /** 20 | * An instance of this interface can be supplied to the pool at its creation time, and its methods will be called 21 | * upon calling the pool {@code take} and {@code restore} operations. 22 | * 23 | * @author Simeon Malchev 24 | */ 25 | public interface Listener { 26 | 27 | void onTake(T object); 28 | 29 | void onRestore(T object, boolean valid); 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/main/java/org/vibur/objectpool/util/MultithreadConcurrentQueueCollection.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool.util; 18 | 19 | import com.conversantmedia.util.concurrent.ConcurrentQueue; 20 | import com.conversantmedia.util.concurrent.MultithreadConcurrentQueue; 21 | 22 | 23 | /** 24 | * A {@link MultithreadConcurrentQueue} based implementation of {@link ConcurrentCollection}. 25 | * 26 | * @author Simeon Malchev 27 | * @param the type of objects held in this {@code ConcurrentCollection} 28 | */ 29 | public class MultithreadConcurrentQueueCollection implements ConcurrentCollection { 30 | 31 | private final ConcurrentQueue queue; 32 | 33 | public MultithreadConcurrentQueueCollection(int capacity) { 34 | queue = new MultithreadConcurrentQueue<>(capacity); 35 | } 36 | 37 | @Override 38 | public void offerFirst(T object) { 39 | offerLast(object); 40 | } 41 | 42 | @Override 43 | public void offerLast(T object) { 44 | queue.offer(object); 45 | } 46 | 47 | @Override 48 | public T pollFirst() { 49 | return queue.poll(); 50 | } 51 | 52 | @Override 53 | public T pollLast() { 54 | return pollFirst(); 55 | } 56 | 57 | @Override 58 | public int size() { 59 | return queue.size(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/vibur/objectpool/util/SamplingPoolReducer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool.util; 18 | 19 | import org.vibur.objectpool.BasePool; 20 | 21 | import java.util.concurrent.TimeUnit; 22 | 23 | import static java.util.Objects.requireNonNull; 24 | import static java.util.concurrent.TimeUnit.NANOSECONDS; 25 | import static org.vibur.objectpool.util.ArgumentValidation.forbidIllegalArgument; 26 | 27 | /** 28 | * A sampling pool reducer util, which is awakened up a given number of times during 29 | * a predefined period of time, and checks whether the number of available 30 | * allocated objects in the object pool needs to be reduced. 31 | * 32 | *

This pool reducer will not bring the number of allocated on the pool 33 | * objects to less than the pool {@code initial} size. 34 | * 35 | *

This pool reducer creates one daemon service thread which will be started when 36 | * the reducer's {@link #start()} method is called, and will be alive until the 37 | * {@link #terminate()} method is called or until the calling application exits. 38 | * 39 | *

Note that if an exception is thrown by the overridable 40 | * {@link #afterReduce(int, int, Throwable)} method hook, it will terminate the 41 | * SamplingPoolReducer, including the reducer's background daemon thread. 42 | * 43 | * @author Simeon Malchev 44 | */ 45 | public class SamplingPoolReducer implements ThreadedPoolReducer { 46 | 47 | private final BasePool pool; 48 | private final long sleepNanoTime; 49 | private final int samples; 50 | 51 | private final Thread reducerThread; 52 | 53 | protected static final double MAX_REDUCTION_FRACTION = 0.2; 54 | protected int minRemainingCreated; 55 | 56 | /** 57 | * Creates a new {@link SamplingPoolReducer} with the given {@link BasePool} and 58 | * {@code timeInterval} settings. The created pool reducer is not started and needs to be 59 | * explicitly started via calling the {@link #start()} method. 60 | * 61 | * @param pool the pool that is to be reduced if necessary 62 | * @param timeInterval the time period after which the {@link SamplingPoolReducer} will try to 63 | * possibly reduce the number of created but unused objects in the 64 | * given {@code pool} 65 | * @param unit the time unit of the {@code timeInterval} argument 66 | * @param samples how many times the {@link SamplingPoolReducer} will wake up during the given 67 | * {@code timeInterval} period in order to sample various information from 68 | * the given {@code pool} 69 | * @throws IllegalArgumentException if one of the following holds:
70 | * {@code timeInterval <= 0 || samples <= 0} 71 | * @throws NullPointerException if one of the following holds:
72 | * {@code pool == null || unit == null} 73 | */ 74 | public SamplingPoolReducer(BasePool pool, long timeInterval, TimeUnit unit, int samples) { 75 | forbidIllegalArgument(timeInterval <= 0, "timeInterval"); 76 | forbidIllegalArgument(samples <= 0, "samples"); 77 | 78 | this.sleepNanoTime = unit.toNanos(timeInterval) / samples; 79 | forbidIllegalArgument(sleepNanoTime == 0, "sleepNanoTime"); 80 | 81 | this.pool = requireNonNull(pool); 82 | this.samples = samples; 83 | 84 | this.reducerThread = new Thread(new PoolReducerRunnable()); 85 | } 86 | 87 | @Override 88 | public void start() { 89 | reducerThread.setName(getThreadName()); 90 | reducerThread.setDaemon(true); 91 | reducerThread.setPriority(Thread.MAX_PRIORITY - 2); 92 | reducerThread.start(); 93 | } 94 | 95 | protected String getThreadName() { 96 | return reducerThread.getName(); 97 | } 98 | 99 | private class PoolReducerRunnable implements Runnable { 100 | @Override 101 | public void run() { 102 | var sample = 1; 103 | minRemainingCreated = Integer.MAX_VALUE; 104 | for (;;) { 105 | try { 106 | NANOSECONDS.sleep(sleepNanoTime); 107 | samplePool(); 108 | if (sample++ % samples == 0) { 109 | reducePool(); 110 | sample = 1; 111 | minRemainingCreated = Integer.MAX_VALUE; 112 | } 113 | } catch (InterruptedException ignored) { 114 | break; 115 | } 116 | } 117 | } 118 | } 119 | 120 | protected void samplePool() { 121 | var remainingCreated = pool.remainingCreated(); 122 | minRemainingCreated = Math.min(minRemainingCreated, remainingCreated); 123 | } 124 | 125 | protected void reducePool() { 126 | var reduction = calculateReduction(); 127 | 128 | var reduced = -1; 129 | Throwable thrown = null; 130 | try { 131 | reduced = pool.reduceCreatedBy(reduction, false); 132 | } catch (Throwable t) { // equivalent to catching "RuntimeException | Error", however, better for Kotlin interoperability 133 | thrown = t; 134 | } finally { 135 | afterReduce(reduction, reduced, thrown); 136 | } 137 | } 138 | 139 | /** 140 | * Calculates the number of currently allocated on the pool elements that needs to be destroyed/deallocated, 141 | * as a result of the stats collected during the just finished observational time period. 142 | * The number of remaining allocated on the pool objects will not fall below the pool {@code initial} 143 | * size as a result of this reduction. 144 | * 145 | * @return the calculated reduction number 146 | */ 147 | protected int calculateReduction() { 148 | var createdTotal = pool.createdTotal(); 149 | var maxReduction = (int) Math.ceil(createdTotal * MAX_REDUCTION_FRACTION); 150 | var reduction = Math.min(minRemainingCreated, maxReduction); 151 | var bottomThreshold = createdTotal - pool.initialSize(); 152 | reduction = Math.min(reduction, bottomThreshold); 153 | return Math.max(reduction, 0); 154 | } 155 | 156 | /** 157 | * An after reduce pool hook. The default implementation will {@code terminate()} this pool reducer 158 | * if {@code thrown != null}. Note that if this method throws an exception, this 159 | * will terminate the pool reducer, too. 160 | * 161 | * @param reduction the intended reduction number 162 | * @param reduced the number of objects which were successfully removed/destroyed from the pool 163 | * @param thrown a thrown during the pool reduction exception if any. 164 | */ 165 | protected void afterReduce(int reduction, int reduced, Throwable thrown) { 166 | if (thrown != null) { 167 | terminate(); 168 | } 169 | } 170 | 171 | @Override 172 | public Thread.State getState() { 173 | return reducerThread.getState(); 174 | } 175 | 176 | @Override 177 | public void terminate() { 178 | reducerThread.interrupt(); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/main/java/org/vibur/objectpool/util/TakenListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool.util; 18 | 19 | import java.util.Collections; 20 | import java.util.Set; 21 | import java.util.concurrent.ConcurrentHashMap; 22 | 23 | /** 24 | * An instance of this class can be supplied to the pool at its creation time, and its methods will be called 25 | * upon calling the pool take and restore operations. This listener can provide a list of all currently taken 26 | * objects from the pool, which can be useful for testing and debugging purposes. 27 | * 28 | * @author Simeon Malchev 29 | */ 30 | public class TakenListener implements Listener { 31 | 32 | private final Set taken; 33 | 34 | public TakenListener() { 35 | taken = Collections.newSetFromMap(new ConcurrentHashMap<>()); 36 | } 37 | 38 | public TakenListener(int initialCapacity) { 39 | taken = Collections.newSetFromMap(new ConcurrentHashMap<>(initialCapacity)); 40 | } 41 | 42 | @Override 43 | public void onTake(T object) { 44 | taken.add(object); 45 | } 46 | 47 | @Override 48 | public void onRestore(T object, boolean valid) { 49 | taken.remove(object); 50 | } 51 | 52 | protected T[] getTaken(T[] a) { 53 | return taken.toArray(a); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/vibur/objectpool/util/ThreadedPoolReducer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool.util; 18 | 19 | /** 20 | * The pool reducers implementing this interface will create one daemon service thread 21 | * which will be started when the reducer's {@link #start()} method is called, and will 22 | * be alive until the {@link #terminate()} method is called or until the calling application 23 | * exits. 24 | * 25 | * @author Simeon Malchev 26 | */ 27 | public interface ThreadedPoolReducer { 28 | 29 | /** 30 | * Starts this pool reducer, which starts its underlying daemon thread. 31 | * 32 | * @exception IllegalThreadStateException if this pool reducer is started more then once 33 | */ 34 | void start(); 35 | 36 | /** 37 | * Returns the state of the underlying thread. 38 | */ 39 | Thread.State getState(); 40 | 41 | /** 42 | * Terminates this pool reducer, which terminates its underlying daemon thread. 43 | * Once terminated the pool reducer cannot be more revived. 44 | */ 45 | void terminate(); 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/org/vibur/objectpool/ConcurrentPoolTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool; 18 | 19 | import org.junit.jupiter.api.AfterEach; 20 | import org.junit.jupiter.api.Test; 21 | import org.vibur.objectpool.util.ConcurrentLinkedDequeCollection; 22 | import org.vibur.objectpool.util.ConcurrentLinkedQueueCollection; 23 | 24 | import static org.junit.jupiter.api.Assertions.assertEquals; 25 | import static org.junit.jupiter.api.Assertions.assertFalse; 26 | import static org.junit.jupiter.api.Assertions.assertNotNull; 27 | import static org.junit.jupiter.api.Assertions.assertNull; 28 | import static org.junit.jupiter.api.Assertions.assertSame; 29 | import static org.junit.jupiter.api.Assertions.assertThrows; 30 | import static org.junit.jupiter.api.Assertions.assertTrue; 31 | import static org.junit.jupiter.api.Assertions.fail; 32 | 33 | /** 34 | * @author Simeon Malchev 35 | */ 36 | public class ConcurrentPoolTest { 37 | 38 | private PoolService pool = null; 39 | 40 | @AfterEach 41 | public void tearDown() { 42 | if (pool != null ) { 43 | pool.terminate(); 44 | } 45 | pool = null; 46 | } 47 | 48 | @Test 49 | public void testSimpleTakes() { 50 | pool = new ConcurrentPool<>(new ConcurrentLinkedQueueCollection<>(), new SimpleObjectFactory(), 1, 3, false); 51 | 52 | var obj1 = pool.take(); 53 | var obj2 = pool.take(); 54 | var obj3 = pool.take(); 55 | var obj4 = pool.tryTake(); 56 | 57 | assertNotNull(obj1); 58 | assertNotNull(obj2); 59 | assertNotNull(obj3); 60 | assertNull(obj4); 61 | 62 | pool.restore(obj1); 63 | pool.restore(obj2); 64 | pool.restore(obj3); 65 | 66 | assertThrows(NullPointerException.class, () -> pool.restore(null)); 67 | } 68 | 69 | @Test 70 | public void testInterrupted() { 71 | pool = new ConcurrentPool<>(new ConcurrentLinkedQueueCollection<>(), new SimpleObjectFactory(), 1, 3, false); 72 | 73 | Thread.currentThread().interrupt(); 74 | var obj1 = pool.take(); 75 | 76 | assertNull(obj1); 77 | assertTrue(Thread.interrupted()); // clears the interrupted flag in order to not affect subsequent tests 78 | } 79 | 80 | @Test 81 | public void testSimpleMetrics() { 82 | pool = new ConcurrentPool<>(new ConcurrentLinkedQueueCollection<>(), new SimpleObjectFactory(), 1, 10, false); 83 | 84 | // tests the initial pool state 85 | assertFalse(pool.isTerminated()); 86 | assertEquals(1, pool.initialSize()); 87 | assertEquals(10, pool.maxSize()); 88 | 89 | assertEquals(1, pool.createdTotal()); 90 | assertEquals(1, pool.remainingCreated()); 91 | assertEquals(10, pool.remainingCapacity()); 92 | assertEquals(0, pool.taken()); 93 | 94 | // takes one object and test 95 | var obj1 = pool.take(); 96 | assertNotNull(obj1); 97 | assertEquals(1, pool.createdTotal()); 98 | assertEquals(0, pool.remainingCreated()); 99 | assertEquals(9, pool.remainingCapacity()); 100 | assertEquals(1, pool.taken()); 101 | 102 | // restores one object and test 103 | pool.restore(obj1); 104 | assertEquals(1, pool.createdTotal()); 105 | assertEquals(1, pool.remainingCreated()); 106 | assertEquals(10, pool.remainingCapacity()); 107 | assertEquals(0, pool.taken()); 108 | 109 | // takes all objects and test 110 | var objs = new Object[10]; 111 | for (var i = 0; i < 10; i++) { 112 | objs[i] = pool.take(); 113 | assertNotNull(objs[i]); 114 | } 115 | obj1 = pool.tryTake(); 116 | assertNull(obj1); 117 | assertEquals(10, pool.createdTotal()); 118 | assertEquals(0, pool.remainingCreated()); 119 | assertEquals(0, pool.remainingCapacity()); 120 | assertEquals(10, pool.taken()); 121 | 122 | // restores the first 6 objects and test 123 | for (var i = 0; i < 6; i++) { 124 | pool.restore(objs[i]); 125 | } 126 | assertEquals(10, pool.createdTotal()); 127 | assertEquals(6, pool.remainingCreated()); 128 | assertEquals(6, pool.remainingCapacity()); 129 | assertEquals(4, pool.taken()); 130 | 131 | // restores the remaining 4 objects and test 132 | for (var i = 6; i < 10; i++) { 133 | pool.restore(objs[i]); 134 | } 135 | assertEquals(10, pool.createdTotal()); 136 | assertEquals(10, pool.remainingCreated()); 137 | assertEquals(10, pool.remainingCapacity()); 138 | assertEquals(0, pool.taken()); 139 | 140 | // terminates the pool and test 141 | pool.terminate(); 142 | assertTrue(pool.isTerminated()); 143 | assertEquals(1, pool.initialSize()); 144 | assertEquals(10, pool.maxSize()); 145 | 146 | assertEquals(0, pool.createdTotal()); 147 | assertEquals(0, pool.remainingCreated()); 148 | assertEquals(0, pool.remainingCapacity()); 149 | assertEquals(0, pool.taken()); 150 | } 151 | 152 | @Test 153 | public void testSimpleMetricsWhenExceptionIsThrownFromObjectFactory() { 154 | var objectFactory = new ExceptionThrowingObjectFactory(); 155 | pool = new ConcurrentPool<>(new ConcurrentLinkedQueueCollection<>(), objectFactory, 1, 10, false); 156 | 157 | // tests the initial pool state 158 | assertFalse(pool.isTerminated()); 159 | assertEquals(1, pool.initialSize()); 160 | assertEquals(10, pool.maxSize()); 161 | 162 | assertEquals(1, pool.createdTotal()); 163 | assertEquals(1, pool.remainingCreated()); 164 | assertEquals(10, pool.remainingCapacity()); 165 | assertEquals(0, pool.taken()); 166 | 167 | objectFactory.throwInReadyToTake = true; 168 | // tries to take one object and test 169 | try { 170 | pool.take(); 171 | fail("Exception expected"); 172 | } catch (Exception e) { 173 | assertSame(Exception.class, e.getClass()); 174 | assertEquals("undeclared checked exception thrown for testing purposes", e.getMessage()); 175 | } 176 | assertEquals(0, pool.createdTotal()); 177 | assertEquals(0, pool.remainingCreated()); 178 | assertEquals(10, pool.remainingCapacity()); 179 | assertEquals(0, pool.taken()); 180 | 181 | objectFactory.throwInReadyToTake = false; 182 | // takes one object and test 183 | var obj1 = pool.take(); 184 | assertNotNull(obj1); 185 | assertEquals(1, pool.createdTotal()); 186 | assertEquals(0, pool.remainingCreated()); 187 | assertEquals(9, pool.remainingCapacity()); 188 | assertEquals(1, pool.taken()); 189 | 190 | // tries to restore the taken object and test 191 | objectFactory.throwInReadyToRestore = true; 192 | try { 193 | pool.restore(obj1); 194 | fail("RuntimeException expected"); 195 | } catch (RuntimeException e) { 196 | assertSame(RuntimeException.class, e.getClass()); 197 | assertEquals("runtime exception thrown for testing purposes", e.getMessage()); 198 | } 199 | assertEquals(0, pool.createdTotal()); 200 | assertEquals(0, pool.remainingCreated()); 201 | assertEquals(10, pool.remainingCapacity()); 202 | assertEquals(0, pool.taken()); 203 | 204 | // terminates the pool and test 205 | pool.terminate(); 206 | assertTrue(pool.isTerminated()); 207 | assertEquals(1, pool.initialSize()); 208 | assertEquals(10, pool.maxSize()); 209 | 210 | assertEquals(0, pool.createdTotal()); 211 | assertEquals(0, pool.remainingCreated()); 212 | assertEquals(0, pool.remainingCapacity()); 213 | assertEquals(0, pool.taken()); 214 | } 215 | 216 | @Test 217 | public void testPoolReductions() { 218 | pool = new ConcurrentPool<>(new ConcurrentLinkedDequeCollection<>(), new SimpleObjectFactory(), 1, 10, false); 219 | 220 | // takes all objects and test 221 | var objs = new Object[10]; 222 | for (var i = 0; i < 10; i++) { 223 | objs[i] = pool.take(); 224 | assertNotNull(objs[i]); 225 | } 226 | var obj1 = pool.tryTake(); 227 | assertNull(obj1); 228 | 229 | assertEquals(1, pool.initialSize()); 230 | assertEquals(10, pool.maxSize()); 231 | 232 | assertEquals(10, pool.createdTotal()); 233 | assertEquals(0, pool.remainingCreated()); 234 | assertEquals(0, pool.remainingCapacity()); 235 | assertEquals(10, pool.taken()); 236 | 237 | // restores all objects and test 238 | for (var i = 0; i < 10; i++) { 239 | pool.restore(objs[i]); 240 | } 241 | assertEquals(10, pool.createdTotal()); 242 | assertEquals(10, pool.remainingCreated()); 243 | assertEquals(10, pool.remainingCapacity()); 244 | assertEquals(0, pool.taken()); 245 | 246 | // reduce the number of the created objects in the pool BY 5 and test 247 | var reduction = pool.reduceCreatedBy(5, false); 248 | assertEquals(5, reduction); 249 | 250 | assertEquals(1, pool.initialSize()); 251 | assertEquals(10, pool.maxSize()); 252 | 253 | assertEquals(5, pool.createdTotal()); 254 | assertEquals(5, pool.remainingCreated()); 255 | assertEquals(10, pool.remainingCapacity()); 256 | assertEquals(0, pool.taken()); 257 | 258 | // now takes again all objects 259 | for (var i = 0; i < 10; i++) { 260 | objs[i] = pool.take(); 261 | assertNotNull(objs[i]); 262 | } 263 | obj1 = pool.tryTake(); 264 | assertNull(obj1); 265 | // then restores again all objects and test 266 | for (var i = 0; i < 10; i++) { 267 | pool.restore(objs[i]); 268 | } 269 | 270 | assertEquals(1, pool.initialSize()); 271 | assertEquals(10, pool.maxSize()); 272 | 273 | assertEquals(10, pool.createdTotal()); 274 | assertEquals(10, pool.remainingCreated()); 275 | assertEquals(10, pool.remainingCapacity()); 276 | assertEquals(0, pool.taken()); 277 | 278 | // reduce the number of the created objects in the pool TO 3 and test 279 | reduction = pool.reduceCreatedTo(3, false); 280 | assertEquals(7, reduction); 281 | 282 | assertEquals(1, pool.initialSize()); 283 | assertEquals(10, pool.maxSize()); 284 | 285 | assertEquals(3, pool.createdTotal()); 286 | assertEquals(3, pool.remainingCreated()); 287 | assertEquals(10, pool.remainingCapacity()); 288 | assertEquals(0, pool.taken()); 289 | 290 | // drain all created objects from the pool and test 291 | var drained = pool.drainCreated(); 292 | assertEquals(3, drained); 293 | 294 | assertEquals(1, pool.initialSize()); 295 | assertEquals(10, pool.maxSize()); 296 | 297 | assertEquals(0, pool.createdTotal()); 298 | assertEquals(0, pool.remainingCreated()); 299 | assertEquals(10, pool.remainingCapacity()); 300 | assertEquals(0, pool.taken()); 301 | 302 | // now takes 5 objects and test 303 | for (var i = 0; i < 5; i++) { 304 | objs[i] = pool.take(); 305 | assertNotNull(objs[i]); 306 | } 307 | assertEquals(1, pool.initialSize()); 308 | assertEquals(10, pool.maxSize()); 309 | 310 | assertEquals(5, pool.createdTotal()); 311 | assertEquals(0, pool.remainingCreated()); 312 | assertEquals(5, pool.remainingCapacity()); 313 | assertEquals(5, pool.taken()); 314 | } 315 | 316 | @Test 317 | public void testNoValidations() { 318 | pool = new ConcurrentPool<>(new ConcurrentLinkedDequeCollection<>(), new SimpleObjectFactory(), 1, 2, false); 319 | 320 | // takes one object and test 321 | var obj1 = pool.take(); 322 | assertNotNull(obj1); 323 | // takes second object and test 324 | var obj2 = pool.take(); 325 | assertNotNull(obj2); 326 | // tries to take third object and test 327 | var obj3 = pool.tryTake(); 328 | assertNull(obj3); 329 | 330 | pool.restore(obj1); 331 | pool.restore(obj2); 332 | 333 | // this pool doesn't provide any validation of the restored objects and allows 334 | // an object which is not taken from the pool to be restored to it, 335 | // even if the pool remainingCapacity() will increase above its maxSize() and createdTotal(). 336 | pool.restore(new Object()); 337 | 338 | assertEquals(3, pool.remainingCapacity()); 339 | assertEquals(2, pool.createdTotal()); 340 | assertEquals(2, pool.maxSize()); 341 | } 342 | 343 | @Test 344 | public void testTimeWaited() { 345 | pool = new ConcurrentPool<>(new ConcurrentLinkedQueueCollection<>(), new SimpleObjectFactory(), 1, 2, false); 346 | 347 | // takes one object and test 348 | long[] timeWaited = {-1}; 349 | var obj1 = pool.take(timeWaited); 350 | 351 | assertNotNull(obj1); 352 | assertTrue(timeWaited[0] >= 0); 353 | } 354 | } 355 | -------------------------------------------------------------------------------- /src/test/java/org/vibur/objectpool/ExceptionThrowingObjectFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool; 18 | 19 | /** 20 | * @author Simeon Malchev 21 | */ 22 | public class ExceptionThrowingObjectFactory implements PoolObjectFactory { 23 | 24 | boolean throwInReadyToTake = false; 25 | boolean throwInReadyToRestore = false; 26 | 27 | @SuppressWarnings("unchecked") 28 | private static void throwAny(Throwable t) throws T { 29 | throw (T) t; 30 | } 31 | 32 | @Override 33 | public Object create() { 34 | return new Object(); 35 | } 36 | 37 | @Override 38 | public boolean readyToTake(Object obj) { 39 | if (throwInReadyToTake) { 40 | throwAny(new Exception("undeclared checked exception thrown for testing purposes")); 41 | } 42 | return true; 43 | } 44 | 45 | @Override 46 | public boolean readyToRestore(Object obj) { 47 | if (throwInReadyToRestore) { 48 | throw new RuntimeException("runtime exception thrown for testing purposes"); 49 | } 50 | return true; 51 | } 52 | 53 | @Override 54 | public void destroy(Object obj) { 55 | // do nothing 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/org/vibur/objectpool/SimpleObjectFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool; 18 | 19 | /** 20 | * @author Simeon Malchev 21 | */ 22 | public class SimpleObjectFactory implements PoolObjectFactory { 23 | 24 | @Override 25 | public Object create() { 26 | return new Object(); 27 | } 28 | 29 | @Override 30 | public boolean readyToTake(Object obj) { 31 | return true; 32 | } 33 | 34 | @Override 35 | public boolean readyToRestore(Object obj) { 36 | return true; 37 | } 38 | 39 | @Override 40 | public void destroy(Object obj) { 41 | // do nothing 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/org/vibur/objectpool/perf/ConcurrentPoolTestPerf.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool.perf; 18 | 19 | import org.vibur.objectpool.ConcurrentPool; 20 | import org.vibur.objectpool.PoolService; 21 | import org.vibur.objectpool.SimpleObjectFactory; 22 | import org.vibur.objectpool.util.ConcurrentLinkedDequeCollection; 23 | 24 | import java.util.concurrent.CountDownLatch; 25 | import java.util.concurrent.TimeUnit; 26 | import java.util.concurrent.atomic.AtomicInteger; 27 | 28 | /** 29 | * @author Simeon Malchev 30 | */ 31 | public class ConcurrentPoolTestPerf { 32 | 33 | // pool metrics: 34 | private static final int INITIAL_SIZE = 50; 35 | private static final int MAX_SIZE = 200; 36 | private static final long TIMEOUT_MS = 2000; 37 | private static final boolean FAIR = false; 38 | 39 | // threads metrics: 40 | private static final int ITERATIONS = 100; 41 | private static final int THREADS_COUNT = 500; 42 | private static final long DO_WORK_FOR_MS = 2; 43 | 44 | public static void main(String[] args) throws InterruptedException { 45 | 46 | // Creates a ConcurrentLinkedPool with an INITIAL_SIZE and a MAX_SIZE, and starts a THREADS_COUNT threads 47 | // where each thread executes ITERATIONS times the following code: 48 | // 49 | // Object obj = pool.tryTake(TIMEOUT_MS, TimeUnit.MILLISECONDS); 50 | // doWork(DO_WORK_FOR_MS); 51 | // pool.restore(obj); 52 | // 53 | // Each tryTake() call has a TIMEOUT_MS and the number of unsuccessful takes is recorded. 54 | // Measures and reports the total time taken by the test in ms. 55 | 56 | PoolService pool = new ConcurrentPool<>(new ConcurrentLinkedDequeCollection<>(), 57 | new SimpleObjectFactory(), INITIAL_SIZE, MAX_SIZE, FAIR, null); 58 | 59 | var errors = new AtomicInteger(0); 60 | 61 | var startSignal = new CountDownLatch(1); 62 | var readySignal = new CountDownLatch(THREADS_COUNT); 63 | var doneSignal = new CountDownLatch(THREADS_COUNT); 64 | 65 | for (var i = 0; i < THREADS_COUNT; i++) { 66 | var thread = new Thread(new Worker(pool, errors, DO_WORK_FOR_MS, TIMEOUT_MS, readySignal, startSignal, doneSignal)); 67 | thread.start(); 68 | } 69 | 70 | readySignal.await(); 71 | var start = System.nanoTime(); 72 | startSignal.countDown(); 73 | doneSignal.await(); 74 | 75 | System.out.printf("Total execution time %f ms, unsuccessful takes %d.%n", 76 | (System.nanoTime() - start) * 0.000_001, errors.get()); 77 | 78 | pool.terminate(); 79 | } 80 | 81 | private static class Worker implements Runnable { 82 | private final PoolService pool; 83 | private final AtomicInteger errors; 84 | private final long millis; 85 | private final long timeout; 86 | 87 | private final CountDownLatch readySignal; 88 | private final CountDownLatch startSignal; 89 | private final CountDownLatch doneSignal; 90 | 91 | private Worker(PoolService pool, AtomicInteger errors, long millis, long timeout, 92 | CountDownLatch readySignal, CountDownLatch startSignal, CountDownLatch doneSignal) { 93 | this.pool = pool; 94 | this.errors = errors; 95 | this.millis = millis; 96 | this.timeout = timeout; 97 | this.startSignal = startSignal; 98 | this.readySignal = readySignal; 99 | this.doneSignal = doneSignal; 100 | } 101 | 102 | @Override 103 | public void run() { 104 | try { 105 | readySignal.countDown(); 106 | startSignal.await(); 107 | 108 | for (var i = 0; i < ITERATIONS; i++) { 109 | var obj = pool.tryTake(timeout, TimeUnit.MILLISECONDS); 110 | if (obj != null) { 111 | doWork(millis); 112 | pool.restore(obj); 113 | } 114 | else { 115 | errors.incrementAndGet(); 116 | } 117 | } 118 | } catch (InterruptedException ignored) { 119 | errors.incrementAndGet(); 120 | } finally { 121 | doneSignal.countDown(); 122 | } 123 | } 124 | } 125 | 126 | private static void doWork(long millis) { 127 | if (millis <= 0) { 128 | return; 129 | } 130 | 131 | try { 132 | Thread.sleep(millis); 133 | } catch (InterruptedException ignored) { } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/test/java/org/vibur/objectpool/util/SamplingPoolReducerTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Simeon Malchev 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.vibur.objectpool.util; 18 | 19 | import org.junit.jupiter.api.AfterEach; 20 | import org.junit.jupiter.api.Test; 21 | 22 | import org.vibur.objectpool.ConcurrentPool; 23 | import org.vibur.objectpool.PoolService; 24 | import org.vibur.objectpool.SimpleObjectFactory; 25 | 26 | import java.util.concurrent.CountDownLatch; 27 | import java.util.concurrent.TimeUnit; 28 | 29 | import static org.junit.jupiter.api.Assertions.assertEquals; 30 | import static org.junit.jupiter.api.Assertions.assertNotNull; 31 | 32 | /** 33 | * @author Simeon Malchev 34 | */ 35 | public class SamplingPoolReducerTest { 36 | 37 | private PoolService pool = null; 38 | 39 | @AfterEach 40 | public void tearDown() { 41 | if (pool != null ) { 42 | pool.terminate(); 43 | } 44 | pool = null; 45 | } 46 | 47 | @Test 48 | public void testPoolShrinking() throws InterruptedException { 49 | pool = new ConcurrentPool<>(new ConcurrentLinkedDequeCollection<>(), new SimpleObjectFactory(), 10, 100, false); 50 | 51 | // tests the initial pool state 52 | assertEquals(10, pool.initialSize()); 53 | assertEquals(100, pool.maxSize()); 54 | 55 | assertEquals(10, pool.createdTotal()); 56 | assertEquals(10, pool.remainingCreated()); 57 | assertEquals(100, pool.remainingCapacity()); 58 | assertEquals(0, pool.taken()); 59 | 60 | // takes 90 objects and test 61 | var objs = new Object[90]; 62 | for (var i = 0; i < 90; i++) { 63 | objs[i] = pool.take(); 64 | assertNotNull(objs[i]); 65 | } 66 | assertEquals(90, pool.createdTotal()); 67 | assertEquals(0, pool.remainingCreated()); 68 | assertEquals(10, pool.remainingCapacity()); 69 | assertEquals(90, pool.taken()); 70 | 71 | // restores 30 objects and test 72 | for (var i = 0; i < 30; i++) { 73 | pool.restore(objs[i]); 74 | } 75 | assertEquals(90, pool.createdTotal()); 76 | assertEquals(30, pool.remainingCreated()); 77 | assertEquals(40, pool.remainingCapacity()); 78 | assertEquals(60, pool.taken()); 79 | 80 | // creates, starts and then terminates the pool reducer 81 | final var finishLatch = new CountDownLatch(2); 82 | ThreadedPoolReducer poolReducer = new SamplingPoolReducer(pool, 400, TimeUnit.MILLISECONDS, 500) { 83 | @Override 84 | protected void afterReduce(int reduction, int reduced, Throwable thrown) { 85 | super.afterReduce(reduction, reduced, thrown); 86 | finishLatch.countDown(); 87 | } 88 | }; 89 | poolReducer.start(); 90 | finishLatch.await(); 91 | poolReducer.terminate(); 92 | 93 | // Tests the pool metrics after the reducer was called 2 times. 94 | // Maximum allowed reduction of 20% of 90 will apply on the first call, 95 | // and on the second call the reduction will be 12, as this is the number 96 | // of remaining created elements in the pool (smaller than 20% of 72 which is 14.4), 97 | // i.e. 90 - 18 - 12 = 60 elements created total. 98 | assertEquals(60, pool.createdTotal()); 99 | assertEquals(0, pool.remainingCreated()); 100 | assertEquals(40, pool.remainingCapacity()); 101 | assertEquals(60, pool.taken()); 102 | } 103 | } 104 | --------------------------------------------------------------------------------