4343import java .util .Timer ;
4444import java .util .TimerTask ;
4545import java .util .concurrent .CountDownLatch ;
46- import java .util .concurrent .CyclicBarrier ;
4746import java .util .concurrent .ExecutorService ;
4847import java .util .concurrent .Executors ;
4948import java .util .concurrent .FutureTask ;
@@ -1011,12 +1010,11 @@ void testAddObjectCanAddToMaxIdle() throws Exception {
10111010 }
10121011
10131012 @ RepeatedTest (10 )
1014- @ Timeout (value = 10 , unit = TimeUnit .SECONDS )
1013+ @ Timeout (value = 30 , unit = TimeUnit .SECONDS )
10151014 void testReturnObjectConcurrentCallsRespectsMaxIdleLimit () throws Exception {
10161015 final int maxIdleLimit = 5 ;
1017- final int numThreads = 100 ;
1018- final CyclicBarrier barrier = new CyclicBarrier (numThreads );
1019- final CountDownLatch doneLatch = new CountDownLatch (numThreads );
1016+ // Use more threads than CPU cores to increase contention
1017+ final int numThreads = Math .max (100 , Runtime .getRuntime ().availableProcessors () * 10 );
10201018
10211019 final GenericObjectPoolConfig <String > config = new GenericObjectPoolConfig <>();
10221020 config .setJmxEnabled (false );
@@ -1026,39 +1024,61 @@ void testReturnObjectConcurrentCallsRespectsMaxIdleLimit() throws Exception {
10261024 pool .setMinIdle (-1 );
10271025 pool .setMaxTotal (-1 );
10281026
1029- final List < Runnable > tasks = new ArrayList <>();
1030-
1027+ // Pre-borrow all objects in the main thread to eliminate variability
1028+ final List < String > borrowedObjects = new ArrayList <>();
10311029 for (int i = 0 ; i < numThreads ; i ++) {
1032- tasks .add (() -> {
1033- try {
1034- final String pooledObject = pool .borrowObject ();
1035- barrier .await (); // Wait for all threads to be ready
1036- pool .returnObject (pooledObject );
1037- } catch (final Exception e ) {
1038- // do nothing
1039- } finally {
1040- doneLatch .countDown ();
1041- }
1042- });
1030+ borrowedObjects .add (pool .borrowObject ());
10431031 }
1032+ assertEquals (numThreads , pool .getNumActive (), "All objects should be active" );
1033+ assertEquals (0 , pool .getNumIdle (), "No objects should be idle yet" );
1034+
1035+ // Use AtomicBoolean with busy-spin for tighter synchronization than CountDownLatch
1036+ final AtomicBoolean startFlag = new AtomicBoolean (false );
1037+ final AtomicInteger readyCount = new AtomicInteger (0 );
1038+ final CountDownLatch doneLatch = new CountDownLatch (numThreads );
10441039
10451040 final ExecutorService executorService = Executors .newFixedThreadPool (numThreads );
10461041 try {
1047- tasks .forEach (executorService ::submit );
1048- doneLatch .await ();
1042+ // Create tasks that will return objects concurrently
1043+ for (final String obj : borrowedObjects ) {
1044+ executorService .submit (() -> {
1045+ try {
1046+ // Signal ready and busy-spin until start flag is set
1047+ readyCount .incrementAndGet ();
1048+ while (!startFlag .get ()) {
1049+ Thread .yield (); // Hint to the JVM that we're spin-waiting
1050+ }
1051+ pool .returnObject (obj );
1052+ } finally {
1053+ doneLatch .countDown ();
1054+ }
1055+ });
1056+ }
1057+
1058+ // Wait for all threads to be ready (busy-spin waiting)
1059+ while (readyCount .get () < numThreads ) {
1060+ Thread .yield ();
1061+ }
1062+
1063+ // Small delay to ensure all threads are truly in their spin-wait loops
1064+ Thread .sleep (10 );
1065+
1066+ // Signal all threads to start returning simultaneously
1067+ startFlag .set (true );
1068+
1069+ // Wait for all returns to complete
1070+ assertTrue (doneLatch .await (10 , TimeUnit .SECONDS ),
1071+ "Not all threads completed in time" );
1072+
10491073 executorService .shutdown ();
1050- assertTrue (executorService .awaitTermination (60 , TimeUnit .SECONDS ),
1074+ assertTrue (executorService .awaitTermination (5 , TimeUnit .SECONDS ),
10511075 "Executor did not terminate in time" );
10521076 } finally {
1053- executorService .shutdownNow (); // Ensure cleanup
1077+ executorService .shutdownNow ();
10541078 }
10551079
10561080 assertTrue (pool .getNumIdle () <= maxIdleLimit ,
1057- "Concurrent addObject() calls should not exceed maxIdle limit of " + maxIdleLimit +
1058- ", but found " + pool .getNumIdle () + " idle objects" );
1059-
1060- assertTrue (pool .getNumIdle () >= pool .getMinIdle (),
1061- "Concurrent addObject() calls should not go below minIdle limit of " + pool .getMinIdle () +
1081+ "Concurrent returnObject() calls should not exceed maxIdle limit of " + maxIdleLimit +
10621082 ", but found " + pool .getNumIdle () + " idle objects" );
10631083 }
10641084 }
0 commit comments