{
+
+ T get() throws IOException;
+}
diff --git a/src/main/java/org/warp/commonutils/functional/TriConsumer.java b/src/main/java/org/warp/commonutils/functional/TriConsumer.java
new file mode 100644
index 0000000..e7c19bb
--- /dev/null
+++ b/src/main/java/org/warp/commonutils/functional/TriConsumer.java
@@ -0,0 +1,54 @@
+package org.warp.commonutils.functional;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Represents an operation that accepts three input arguments and returns no
+ * result. This is the three-arity specialization of {@link Consumer}.
+ * Unlike most other functional interfaces, {@code TriConsumer} is expected
+ * to operate via side-effects.
+ *
+ * This is a functional interface
+ * whose functional method is {@link #accept(Object, Object, Object)}.
+ *
+ * @param the type of the first argument to the operation
+ * @param the type of the second argument to the operation
+ * @param the type of the thord argument to the operation
+ *
+ * @see Consumer
+ * @since 1.8
+ */
+@FunctionalInterface
+public interface TriConsumer {
+
+ /**
+ * Performs this operation on the given arguments.
+ *
+ * @param t the first input argument
+ * @param u the second input argument
+ * @param v the third input argument
+ */
+ void accept(T t, U u, V v);
+
+ /**
+ * Returns a composed {@code TriConsumer} that performs, in sequence, this
+ * operation followed by the {@code after} operation. If performing either
+ * operation throws an exception, it is relayed to the caller of the
+ * composed operation. If performing this operation throws an exception,
+ * the {@code after} operation will not be performed.
+ *
+ * @param after the operation to perform after this operation
+ * @return a composed {@code TriConsumer} that performs in sequence this
+ * operation followed by the {@code after} operation
+ * @throws NullPointerException if {@code after} is null
+ */
+ default org.warp.commonutils.functional.TriConsumer andThen(org.warp.commonutils.functional.TriConsumer super T, ? super U, ? super V> after) {
+ Objects.requireNonNull(after);
+
+ return (l, r, u) -> {
+ accept(l, r, u);
+ after.accept(l, r, u);
+ };
+ }
+}
diff --git a/src/main/java/org/warp/commonutils/functional/TriFunction.java b/src/main/java/org/warp/commonutils/functional/TriFunction.java
new file mode 100644
index 0000000..3e5abf9
--- /dev/null
+++ b/src/main/java/org/warp/commonutils/functional/TriFunction.java
@@ -0,0 +1,51 @@
+package org.warp.commonutils.functional;
+
+import java.util.Objects;
+import java.util.function.Function;
+
+/**
+ * Represents a function that accepts three arguments and produces a result.
+ * This is the three-arity specialization of {@link Function}.
+ *
+ * This is a functional interface
+ * whose functional method is {@link #apply(Object, Object, Object)}.
+ *
+ * @param the type of the first argument to the function
+ * @param the type of the second argument to the function
+ * @param the type of the third argument to the function
+ * @param the type of the result of the function
+ *
+ * @see Function
+ * @since 1.8
+ */
+@FunctionalInterface
+public interface TriFunction {
+
+ /**
+ * Applies this function to the given arguments.
+ *
+ * @param t the first function argument
+ * @param u the second function argument
+ * @param x the third function argument
+ * @return the function result
+ */
+ R apply(T t, U u, X x);
+
+ /**
+ * Returns a composed function that first applies this function to
+ * its input, and then applies the {@code after} function to the result.
+ * If evaluation of either function throws an exception, it is relayed to
+ * the caller of the composed function.
+ *
+ * @param the type of output of the {@code after} function, and of the
+ * composed function
+ * @param after the function to apply after this function is applied
+ * @return a composed function that first applies this function and then
+ * applies the {@code after} function
+ * @throws NullPointerException if after is null
+ */
+ default org.warp.commonutils.functional.TriFunction andThen(Function super R, ? extends V> after) {
+ Objects.requireNonNull(after);
+ return (T t, U u, X x) -> after.apply(apply(t, u, x));
+ }
+}
diff --git a/src/main/java/org/warp/commonutils/functional/Unchecked.java b/src/main/java/org/warp/commonutils/functional/Unchecked.java
new file mode 100644
index 0000000..2e5ea64
--- /dev/null
+++ b/src/main/java/org/warp/commonutils/functional/Unchecked.java
@@ -0,0 +1,30 @@
+package org.warp.commonutils.functional;
+
+import java.util.function.Function;
+
+public class Unchecked implements Function {
+
+ private final UncheckedConsumer uncheckedConsumer;
+
+ public Unchecked(UncheckedConsumer uncheckedConsumer) {
+ this.uncheckedConsumer = uncheckedConsumer;
+ }
+
+ public static Unchecked wrap(UncheckedConsumer uncheckedConsumer) {
+ return new Unchecked<>(uncheckedConsumer);
+ }
+
+ @Override
+ public UncheckedResult apply(T t) {
+ try {
+ uncheckedConsumer.consume(t);
+ return new UncheckedResult();
+ } catch (Exception e) {
+ return new UncheckedResult(e);
+ }
+ }
+
+ public interface UncheckedConsumer {
+ public void consume(T value) throws Exception;
+ }
+}
diff --git a/src/main/java/org/warp/commonutils/functional/UncheckedResult.java b/src/main/java/org/warp/commonutils/functional/UncheckedResult.java
new file mode 100644
index 0000000..853f811
--- /dev/null
+++ b/src/main/java/org/warp/commonutils/functional/UncheckedResult.java
@@ -0,0 +1,33 @@
+package org.warp.commonutils.functional;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class UncheckedResult {
+
+ @Nullable
+ private final Exception e;
+
+ public UncheckedResult(@NotNull Exception e) {
+ this.e = e;
+ }
+
+ public UncheckedResult() {
+ this.e = null;
+ }
+
+ public UncheckedResult throwException(@NotNull Class exceptionClass) throws T {
+ if (e != null) {
+ if (exceptionClass.isInstance(e)) {
+ throw (T) e;
+ }
+ }
+ return this;
+ }
+
+ public void done() {
+ if (e != null) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/main/java/org/warp/commonutils/functional/UnsafeIOUtils.java b/src/main/java/org/warp/commonutils/functional/UnsafeIOUtils.java
new file mode 100644
index 0000000..ed02a16
--- /dev/null
+++ b/src/main/java/org/warp/commonutils/functional/UnsafeIOUtils.java
@@ -0,0 +1,52 @@
+package org.warp.commonutils.functional;
+
+import java.io.IOError;
+import java.io.IOException;
+import org.warp.commonutils.functional.IOBooleanSupplier;
+import org.warp.commonutils.functional.IOIntegerSupplier;
+import org.warp.commonutils.functional.IOLongSupplier;
+import org.warp.commonutils.functional.IORunnable;
+import org.warp.commonutils.functional.IOSupplier;
+
+public final class UnsafeIOUtils {
+
+ public static T unsafe(IOSupplier expression) {
+ try {
+ return expression.get();
+ } catch (IOException e) {
+ throw new IOError(e);
+ }
+ }
+
+ public static int unsafe(IOIntegerSupplier expression) {
+ try {
+ return expression.get();
+ } catch (IOException e) {
+ throw new IOError(e);
+ }
+ }
+
+ public static boolean unsafe(IOBooleanSupplier expression) {
+ try {
+ return expression.get();
+ } catch (IOException e) {
+ throw new IOError(e);
+ }
+ }
+
+ public static long unsafe(IOLongSupplier expression) {
+ try {
+ return expression.get();
+ } catch (IOException e) {
+ throw new IOError(e);
+ }
+ }
+
+ public static void unsafe(IORunnable expression) {
+ try {
+ expression.run();
+ } catch (IOException e) {
+ throw new IOError(e);
+ }
+ }
+}
diff --git a/src/main/java/org/warp/commonutils/locks/LeftRightLock.java b/src/main/java/org/warp/commonutils/locks/LeftRightLock.java
new file mode 100644
index 0000000..7b8d458
--- /dev/null
+++ b/src/main/java/org/warp/commonutils/locks/LeftRightLock.java
@@ -0,0 +1,123 @@
+package org.warp.commonutils.locks;
+import java.util.concurrent.locks.AbstractQueuedSynchronizer;
+import java.util.concurrent.locks.Lock;
+
+/**
+ * A binary mutex with the following properties:
+ *
+ * Exposes two different {@link Lock}s: LEFT, RIGHT.
+ *
+ * When LEFT is held other threads can acquire LEFT but thread trying to acquire RIGHT will be
+ * blocked. When RIGHT is held other threads can acquire RIGHT but thread trying to acquire LEFT
+ * will be blocked.
+ */
+public class LeftRightLock {
+
+ public static final int ACQUISITION_FAILED = -1;
+ public static final int ACQUISITION_SUCCEEDED = 1;
+
+ private final LeftRightSync sync = new LeftRightSync();
+
+ public void lockLeft() {
+ sync.acquireShared(LockSide.LEFT.getV());
+ }
+
+ public void lockRight() {
+ sync.acquireShared(LockSide.RIGHT.getV());
+ }
+
+ public void releaseLeft() {
+ sync.releaseShared(LockSide.LEFT.getV());
+ }
+
+ public void releaseRight() {
+ sync.releaseShared(LockSide.RIGHT.getV());
+ }
+
+ public boolean tryLockLeft() {
+ return sync.tryAcquireShared(LockSide.LEFT) == ACQUISITION_SUCCEEDED;
+ }
+
+ public boolean tryLockRight() {
+ return sync.tryAcquireShared(LockSide.RIGHT) == ACQUISITION_SUCCEEDED;
+ }
+
+ private enum LockSide {
+ LEFT(-1), NONE(0), RIGHT(1);
+
+ private final int v;
+
+ LockSide(int v) {
+ this.v = v;
+ }
+
+ public int getV() {
+ return v;
+ }
+ }
+
+ /**
+ *
+ * Keep count the count of threads holding either the LEFT or the RIGHT lock.
+ *
+ *
+ * A state ({@link AbstractQueuedSynchronizer#getState()}) greater than 0 means one or more threads are holding RIGHT lock.
+ * A state ({@link AbstractQueuedSynchronizer#getState()}) lower than 0 means one or more threads are holding LEFT lock.
+ * A state ({@link AbstractQueuedSynchronizer#getState()}) equal to zero means no thread is holding any lock.
+ */
+ private static final class LeftRightSync extends AbstractQueuedSynchronizer {
+
+
+ @Override
+ protected int tryAcquireShared(int requiredSide) {
+ return (tryChangeThreadCountHoldingCurrentLock(requiredSide, ChangeType.ADD) ? ACQUISITION_SUCCEEDED : ACQUISITION_FAILED);
+ }
+
+ @Override
+ protected boolean tryReleaseShared(int requiredSide) {
+ return tryChangeThreadCountHoldingCurrentLock(requiredSide, ChangeType.REMOVE);
+ }
+
+ public boolean tryChangeThreadCountHoldingCurrentLock(int requiredSide, ChangeType changeType) {
+ if (requiredSide != 1 && requiredSide != -1)
+ throw new AssertionError("You can either lock LEFT or RIGHT (-1 or +1)");
+
+ int curState;
+ int newState;
+ do {
+ curState = this.getState();
+ if (!sameSide(curState, requiredSide)) {
+ return false;
+ }
+
+ if (changeType == ChangeType.ADD) {
+ newState = curState + requiredSide;
+ } else {
+ newState = curState - requiredSide;
+ }
+ //TODO: protect against int overflow (hopefully you won't have so many threads)
+ } while (!this.compareAndSetState(curState, newState));
+ return true;
+ }
+
+ final int tryAcquireShared(LockSide lockSide) {
+ return this.tryAcquireShared(lockSide.getV());
+ }
+
+ final boolean tryReleaseShared(LockSide lockSide) {
+ return this.tryReleaseShared(lockSide.getV());
+ }
+
+ private boolean sameSide(int curState, int requiredSide) {
+ return curState == 0 || sameSign(curState, requiredSide);
+ }
+
+ private boolean sameSign(int a, int b) {
+ return (a >= 0) ^ (b < 0);
+ }
+
+ public enum ChangeType {
+ ADD, REMOVE
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/warp/commonutils/locks/LockUtils.java b/src/main/java/org/warp/commonutils/locks/LockUtils.java
new file mode 100644
index 0000000..7995218
--- /dev/null
+++ b/src/main/java/org/warp/commonutils/locks/LockUtils.java
@@ -0,0 +1,148 @@
+package org.warp.commonutils.locks;
+
+import java.io.IOException;
+import java.util.concurrent.locks.Lock;
+import java.util.function.Supplier;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.warp.commonutils.functional.IORunnable;
+import org.warp.commonutils.functional.IOSupplier;
+
+public class LockUtils {
+
+ public static void lock(@Nullable Lock lock, @NotNull Runnable r) {
+ if (lock != null) {
+ lock.lock();
+ }
+ try {
+ r.run();
+ } finally {
+ if (lock != null) {
+ lock.unlock();
+ }
+ }
+ }
+
+ public static void lock(@Nullable LeftRightLock lock, boolean right, @NotNull Runnable r) {
+ if (lock != null) {
+ if (right) {
+ lock.lockRight();
+ } else {
+ lock.lockLeft();
+ }
+ }
+ try {
+ r.run();
+ } finally {
+ if (lock != null) {
+ if (right) {
+ lock.releaseRight();
+ } else {
+ lock.releaseLeft();
+ }
+ }
+ }
+ }
+
+ public static void lockIO(@Nullable Lock lock, @NotNull IORunnable r) throws IOException {
+ if (lock != null) {
+ lock.lock();
+ }
+ try {
+ r.run();
+ } finally {
+ if (lock != null) {
+ lock.unlock();
+ }
+ }
+ }
+
+ public static void lockIO(@Nullable LeftRightLock lock, boolean right, @NotNull IORunnable r) throws IOException {
+ if (lock != null) {
+ if (right) {
+ lock.lockRight();
+ } else {
+ lock.lockLeft();
+ }
+ }
+ try {
+ r.run();
+ } finally {
+ if (lock != null) {
+ if (right) {
+ lock.releaseRight();
+ } else {
+ lock.releaseLeft();
+ }
+ }
+ }
+ }
+
+ public static T lock(@Nullable Lock lock, @NotNull Supplier r) {
+ if (lock != null) {
+ lock.lock();
+ }
+ try {
+ return r.get();
+ } finally {
+ if (lock != null) {
+ lock.unlock();
+ }
+ }
+ }
+
+ public static T lock(@Nullable LeftRightLock lock, boolean right, @NotNull Supplier r) {
+ if (lock != null) {
+ if (right) {
+ lock.lockRight();
+ } else {
+ lock.lockLeft();
+ }
+ }
+ try {
+ return r.get();
+ } finally {
+ if (lock != null) {
+ if (right) {
+ lock.releaseRight();
+ } else {
+ lock.releaseLeft();
+ }
+ }
+ }
+ }
+
+ public static T lockIO(@Nullable Lock lock, @NotNull IOSupplier r) throws IOException {
+ if (lock != null) {
+ lock.lock();
+ }
+ try {
+ return r.get();
+ } finally {
+ if (lock != null) {
+ lock.unlock();
+ }
+ }
+ }
+
+ public static T lockIO(@Nullable LeftRightLock lock, boolean right, @NotNull IOSupplier r) throws IOException {
+ if (lock != null) {
+ if (right) {
+ lock.lockRight();
+ } else {
+ lock.lockLeft();
+ }
+ }
+ try {
+ return r.get();
+ } finally {
+ if (lock != null) {
+ if (right) {
+ lock.releaseRight();
+ } else {
+ lock.releaseLeft();
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/warp/commonutils/locks/Striped.java b/src/main/java/org/warp/commonutils/locks/Striped.java
new file mode 100644
index 0000000..bbd09ff
--- /dev/null
+++ b/src/main/java/org/warp/commonutils/locks/Striped.java
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2011 The Guava Authors
+ *
+ * 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.
+ */
+
+package org.warp.commonutils.locks;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.GwtIncompatible;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.MapMaker;
+import com.google.common.math.IntMath;
+import com.google.common.primitives.Ints;
+import com.googlecode.concurentlocks.ReadWriteUpdateLock;
+import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.math.RoundingMode;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.atomic.AtomicReferenceArray;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * A striped {@code Lock/Semaphore/ReadWriteLock}. This offers the underlying lock striping similar to that of {@code
+ * ConcurrentHashMap} in a reusable form, and extends it for semaphores and read-write locks. Conceptually, lock
+ * striping is the technique of dividing a lock into many
+ * stripes, 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.
+ *
+ * The guarantee provided by this class is that equal keys lead to the same lock (or semaphore),
+ * i.e. {@code if (key1.equals(key2))} then {@code striped.get(key1) == striped.get(key2)} (assuming {@link
+ * Object#hashCode()} is correctly implemented for the keys). Note that if {@code key1} is
+ * not equal to {@code key2}, it is not guaranteed that
+ * {@code striped.get(key1) != striped.get(key2)}; the elements might nevertheless be mapped to the same lock. The lower
+ * the number of stripes, the higher the probability of this happening.
+ *
+ *
There are three flavors of this class: {@code Striped}, {@code Striped}, and
+ * {@code Striped}. For each type, two implementations are offered: {@linkplain #lock(int) strong} and
+ * {@linkplain #lazyWeakLock(int) weak} {@code Striped}, {@linkplain #semaphore(int, int) strong} and {@linkplain
+ * #lazyWeakSemaphore(int, int) weak} {@code Striped}, and {@linkplain #readWriteLock(int) strong} and
+ * {@linkplain #lazyWeakReadWriteLock(int) weak} {@code Striped}. Strong means that all stripes
+ * (locks/semaphores) are initialized eagerly, and are not reclaimed unless {@code Striped} itself is reclaimable.
+ * Weak means that locks/semaphores are created lazily, and they are allowed to be reclaimed if nobody is
+ * holding on to them. This is useful, for example, if one wants to create a {@code Striped} of many locks, but
+ * worries that in most cases only a small portion of these would be in use.
+ *
+ * Prior to this class, one might be tempted to use {@code Map}, where {@code K}
+ * represents the task. This maximizes concurrency by having each unique key mapped to a unique lock, but also maximizes
+ * memory footprint. On the other extreme, one could use a single lock for all tasks, which minimizes memory footprint
+ * but also minimizes concurrency. Instead of choosing either of these extremes, {@code Striped} allows the user to
+ * trade between required concurrency and memory footprint. For example, if a set of tasks are CPU-bound, one could
+ * easily create a very compact {@code Striped} of {@code availableProcessors() * 4} stripes, instead of possibly
+ * thousands of locks which could be created in a {@code Map} structure.
+ *
+ * @author Dimitris Andreou
+ * @since 13.0
+ */
+@Beta
+@GwtIncompatible
+public abstract class Striped {
+
+ /**
+ * If there are at least this many stripes, we assume the memory usage of a ConcurrentMap will be smaller than a large
+ * array. (This assumes that in the lazy case, most stripes are unused. As always, if many stripes are in use, a
+ * non-lazy striped makes more sense.)
+ */
+ private static final int LARGE_LAZY_CUTOFF = 1024;
+
+ private Striped() {
+ }
+
+ /**
+ * 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 abstract L get(Object key);
+
+ /**
+ * Returns the stripe at the specified index. Valid indexes are 0, inclusively, to {@code size()}, exclusively.
+ *
+ * @param index the index of the stripe to return; must be in {@code [0...size())}
+ * @return the stripe at the specified index
+ */
+ public abstract L getAt(int index);
+
+ /**
+ * Returns the index to which the given key is mapped, so that getAt(indexFor(key)) == get(key).
+ */
+ abstract int indexFor(Object key);
+
+ /**
+ * Returns the total number of stripes in this instance.
+ */
+ public abstract int size();
+
+ /**
+ * Returns the stripes that correspond to the passed objects, in ascending (as per {@link #getAt(int)}) order. Thus,
+ * threads that use the stripes in the order returned by this method are guaranteed to not deadlock each other.
+ *
+ * It should be noted that using a {@code Striped} with relatively few stripes, and
+ * {@code bulkGet(keys)} with a relative large number of keys can cause an excessive number of shared stripes (much
+ * like the birthday paradox, where much fewer than anticipated birthdays are needed for a pair of them to match).
+ * Please consider carefully the implications of the number of stripes, the intended concurrency level, and the
+ * typical number of keys used in a {@code bulkGet(keys)} operation. See Balls
+ * in Bins model for mathematical formulas that can be used to estimate the probability of collisions.
+ *
+ * @param keys arbitrary non-null keys
+ * @return the stripes corresponding to the objects (one per each object, derived by delegating to {@link
+ * #get(Object)}; may contain duplicates), in an increasing index order.
+ */
+ public Iterable bulkGet(Iterable> keys) {
+ // Initially using the array to store the keys, then reusing it to store the respective L's
+ final Object[] array = Iterables.toArray(keys, Object.class);
+ if (array.length == 0) {
+ return ImmutableList.of();
+ }
+ int[] stripes = new int[array.length];
+ for (int i = 0; i < array.length; i++) {
+ stripes[i] = indexFor(array[i]);
+ }
+ Arrays.sort(stripes);
+ // optimize for runs of identical stripes
+ int previousStripe = stripes[0];
+ array[0] = getAt(previousStripe);
+ for (int i = 1; i < array.length; i++) {
+ int currentStripe = stripes[i];
+ if (currentStripe == previousStripe) {
+ array[i] = array[i - 1];
+ } else {
+ array[i] = getAt(currentStripe);
+ previousStripe = currentStripe;
+ }
+ }
+ /*
+ * Note that the returned Iterable holds references to the returned stripes, to avoid
+ * error-prone code like:
+ *
+ * Striped stripedLock = Striped.lazyWeakXXX(...)'
+ * Iterable locks = stripedLock.bulkGet(keys);
+ * for (Lock lock : locks) {
+ * lock.lock();
+ * }
+ * operation();
+ * for (Lock lock : locks) {
+ * lock.unlock();
+ * }
+ *
+ * If we only held the int[] stripes, translating it on the fly to L's, the original locks might
+ * be garbage collected after locking them, ending up in a huge mess.
+ */
+ @SuppressWarnings("unchecked") // we carefully replaced all keys with their respective L's
+ List asList = (List) Arrays.asList(array);
+ return Collections.unmodifiableList(asList);
+ }
+
+ // Static factories
+
+ /**
+ * Creates a {@code Striped} with eagerly initialized, strongly referenced locks. Every lock is reentrant.
+ *
+ * @param stripes the minimum number of stripes (locks) required
+ * @return a new {@code Striped}
+ */
+ public static Striped lock(int stripes) {
+ return new CompactStriped(stripes, new Supplier() {
+ @Override
+ public Lock get() {
+ return new PaddedLock();
+ }
+ });
+ }
+
+ /**
+ * Creates a {@code Striped} with lazily initialized, weakly referenced locks. Every lock is reentrant.
+ *
+ * @param stripes the minimum number of stripes (locks) required
+ * @return a new {@code Striped}
+ */
+ public static Striped lazyWeakLock(int stripes) {
+ return lazy(stripes, new Supplier() {
+ @Override
+ public Lock get() {
+ return new ReentrantLock(false);
+ }
+ });
+ }
+
+ private static Striped lazy(int stripes, Supplier supplier) {
+ return stripes < LARGE_LAZY_CUTOFF ? new SmallLazyStriped(stripes, supplier)
+ : new LargeLazyStriped(stripes, supplier);
+ }
+
+ /**
+ * Creates a {@code Striped} with eagerly initialized, strongly referenced semaphores, with the specified
+ * number of permits.
+ *
+ * @param stripes the minimum number of stripes (semaphores) required
+ * @param permits the number of permits in each semaphore
+ * @return a new {@code Striped}
+ */
+ public static Striped semaphore(int stripes, final int permits) {
+ return new CompactStriped(stripes, new Supplier() {
+ @Override
+ public Semaphore get() {
+ return new PaddedSemaphore(permits);
+ }
+ });
+ }
+
+ /**
+ * Creates a {@code Striped} with lazily initialized, weakly referenced semaphores, with the specified
+ * number of permits.
+ *
+ * @param stripes the minimum number of stripes (semaphores) required
+ * @param permits the number of permits in each semaphore
+ * @return a new {@code Striped}
+ */
+ public static Striped