StripedLazyWeakLock.java
/*
* #%L
* wcm.io
* %%
* Copyright (C) 2023 wcm.io
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package io.wcm.handler.mediasource.dam.impl.metadata.concurrency;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
/**
* A striped {@code Lock}. This offers the underlying lock striping similar
* to that of {@code ConcurrentHashMap} in a reusable form.
* Conceptually, lock striping is the technique of dividing a lock into many
* <i>stripes</i>, increasing the granularity of a single lock and allowing independent operations
* to lock different stripes and proceed concurrently, instead of creating contention for a single
* lock.
* <p>
* This is inspired by Guava's <a href=
* "https://github.com/google/guava/blob/master/guava/src/com/google/common/util/concurrent/Striped.java">Striped</a>,
* but uses Caffeine internally.
* </p>
*/
public final class StripedLazyWeakLock {
private final StripeIndex stripeIndex;
private final LoadingCache<Integer, Lock> locks;
/**
* Creates a {@code Striped<Lock>} with lazily initialized, weakly referenced locks. Every lock is
* reentrant.
* @param stripes the minimum number of stripes (locks) required
*/
public StripedLazyWeakLock(int stripes) {
this.stripeIndex = new StripeIndex(stripes);
this.locks = Caffeine.newBuilder().weakValues().build(key -> new ReentrantLock());
}
/**
* Returns the stripe that corresponds to the passed key. It is always guaranteed that if {@code
* key1.equals(key2)}, then {@code get(key1) == get(key2)}.
* @param key an arbitrary, non-null key
* @return the stripe that the passed key corresponds to
*/
public Lock get(Object key) {
return locks.get(stripeIndex.indexFor(key));
}
}