@@ -422,6 +422,7 @@ public enum CollectionKeyMode {
422422 private final boolean simpleKeysMode ;
423423 private final boolean valueBasedEquality ;
424424 private final boolean caseSensitive ;
425+ private final boolean trackContentionMetrics ;
425426 private static final float DEFAULT_LOAD_FACTOR = 0.75f ;
426427
427428 // Cached hashCode for performance (invalidated on mutations)
@@ -518,6 +519,7 @@ private MultiKeyMap(Builder<V> builder) {
518519 this .simpleKeysMode = builder .simpleKeysMode ;
519520 this .valueBasedEquality = builder .valueBasedEquality ;
520521 this .caseSensitive = builder .caseSensitive ;
522+ this .trackContentionMetrics = builder .trackContentionMetrics ;
521523
522524 for (int i = 0 ; i < STRIPE_COUNT ; i ++) {
523525 stripeLocks [i ] = new ReentrantLock ();
@@ -590,6 +592,7 @@ public static class Builder<V> {
590592 private boolean simpleKeysMode = false ;
591593 private boolean valueBasedEquality = true ; // Default: cross-type numeric matching
592594 private boolean caseSensitive = true ; // Default: case-sensitive string comparison
595+ private boolean trackContentionMetrics = false ; // Default: off for maximum write throughput
593596
594597 // Private constructor - instantiate via MultiKeyMap.builder()
595598 private Builder () {}
@@ -710,6 +713,22 @@ public Builder<V> caseSensitive(boolean caseSensitive) {
710713 return this ;
711714 }
712715
716+ /**
717+ * Enables or disables lock contention metrics tracking.
718+ * <p>When enabled, the map tracks per-stripe lock acquisition counts and contention
719+ * events using atomic counters. This adds measurable overhead (multiple {@code AtomicInteger}
720+ * increments per write operation) but provides diagnostic data accessible via
721+ * {@link MultiKeyMap#printContentionStatistics()}.</p>
722+ * <p>Default is {@code false} (disabled) for maximum write throughput.</p>
723+ *
724+ * @param track {@code true} to enable contention tracking, {@code false} to disable
725+ * @return this builder instance for method chaining
726+ */
727+ public Builder <V > trackContentionMetrics (boolean track ) {
728+ this .trackContentionMetrics = track ;
729+ return this ;
730+ }
731+
713732 /**
714733 * Copies configuration from an existing MultiKeyMap.
715734 * <p>This copies all configuration settings including capacity, load factor,
@@ -726,6 +745,7 @@ public Builder<V> from(MultiKeyMap<?> source) {
726745 this .simpleKeysMode = source .simpleKeysMode ;
727746 this .valueBasedEquality = source .valueBasedEquality ;
728747 this .caseSensitive = source .caseSensitive ;
748+ this .trackContentionMetrics = source .trackContentionMetrics ;
729749 return this ;
730750 }
731751
@@ -992,16 +1012,21 @@ private ReentrantLock getStripeLock(int hash) {
9921012 }
9931013
9941014 private void lockAllStripes () {
995- int contended = 0 ;
996- for (ReentrantLock lock : stripeLocks ) {
997- // Use tryLock() to accurately detect contention
998- if (!lock .tryLock ()) {
999- contended ++;
1000- lock .lock (); // Now wait for the lock
1015+ if (trackContentionMetrics ) {
1016+ int contended = 0 ;
1017+ for (ReentrantLock lock : stripeLocks ) {
1018+ if (!lock .tryLock ()) {
1019+ contended ++;
1020+ lock .lock ();
1021+ }
1022+ }
1023+ globalLockAcquisitions .incrementAndGet ();
1024+ if (contended > 0 ) globalLockContentions .incrementAndGet ();
1025+ } else {
1026+ for (ReentrantLock lock : stripeLocks ) {
1027+ lock .lock ();
10011028 }
10021029 }
1003- globalLockAcquisitions .incrementAndGet ();
1004- if (contended > 0 ) globalLockContentions .incrementAndGet ();
10051030 }
10061031
10071032 private void unlockAllStripes () {
@@ -3114,23 +3139,29 @@ private V putInternal(MultiKey<V> newKey) {
31143139 V old ;
31153140 boolean resize ;
31163141
3117- // Use tryLock() to accurately detect contention
3118- boolean contended = !lock .tryLock ();
3119- if (contended ) {
3120- // Failed to acquire immediately - this is true contention
3121- lock .lock (); // Now wait for the lock
3122- contentionCount .incrementAndGet ();
3123- stripeLockContention [stripe ].incrementAndGet ();
3124- }
3125-
3126- try {
3127- totalLockAcquisitions .incrementAndGet ();
3128- stripeLockAcquisitions [stripe ].incrementAndGet ();
3129-
3130- old = putNoLock (newKey );
3131- resize = atomicSize .get () > buckets .length () * loadFactor ;
3132- } finally {
3133- lock .unlock ();
3142+ if (trackContentionMetrics ) {
3143+ boolean contended = !lock .tryLock ();
3144+ if (contended ) {
3145+ lock .lock ();
3146+ contentionCount .incrementAndGet ();
3147+ stripeLockContention [stripe ].incrementAndGet ();
3148+ }
3149+ try {
3150+ totalLockAcquisitions .incrementAndGet ();
3151+ stripeLockAcquisitions [stripe ].incrementAndGet ();
3152+ old = putNoLock (newKey );
3153+ resize = atomicSize .get () > buckets .length () * loadFactor ;
3154+ } finally {
3155+ lock .unlock ();
3156+ }
3157+ } else {
3158+ lock .lock ();
3159+ try {
3160+ old = putNoLock (newKey );
3161+ resize = atomicSize .get () > buckets .length () * loadFactor ;
3162+ } finally {
3163+ lock .unlock ();
3164+ }
31343165 }
31353166
31363167 resizeRequest (resize );
@@ -3355,22 +3386,27 @@ private V removeInternal(final MultiKey<V> removeKey) {
33553386 ReentrantLock lock = stripeLocks [stripe ];
33563387 V old ;
33573388
3358- // Use tryLock() to accurately detect contention
3359- boolean contended = !lock .tryLock ();
3360- if (contended ) {
3361- // Failed to acquire immediately - this is true contention
3362- lock .lock (); // Now wait for the lock
3363- contentionCount .incrementAndGet ();
3364- stripeLockContention [stripe ].incrementAndGet ();
3365- }
3366-
3367- try {
3368- totalLockAcquisitions .incrementAndGet ();
3369- stripeLockAcquisitions [stripe ].incrementAndGet ();
3370-
3371- old = removeNoLock (removeKey );
3372- } finally {
3373- lock .unlock ();
3389+ if (trackContentionMetrics ) {
3390+ boolean contended = !lock .tryLock ();
3391+ if (contended ) {
3392+ lock .lock ();
3393+ contentionCount .incrementAndGet ();
3394+ stripeLockContention [stripe ].incrementAndGet ();
3395+ }
3396+ try {
3397+ totalLockAcquisitions .incrementAndGet ();
3398+ stripeLockAcquisitions [stripe ].incrementAndGet ();
3399+ old = removeNoLock (removeKey );
3400+ } finally {
3401+ lock .unlock ();
3402+ }
3403+ } else {
3404+ lock .lock ();
3405+ try {
3406+ old = removeNoLock (removeKey );
3407+ } finally {
3408+ lock .unlock ();
3409+ }
33743410 }
33753411
33763412 return old ;
0 commit comments