1 package uk.org.lidalia.lang;
2
3 import static java.lang.Thread.currentThread;
4 import static java.util.Objects.requireNonNull;
5 import static java.util.Optional.ofNullable;
6
7 import java.util.Collection;
8 import java.util.Map;
9 import java.util.concurrent.ConcurrentHashMap;
10 import java.util.function.Supplier;
11
12 /**
13 * This class was written by Robert Elliot and is originally located at
14 * https://github.com/Mahoney/lidalia-lang
15 *
16 * <p>A ThreadLocal that has no {@link ClassLoader} leaks associated with it and does not permit
17 * null.
18 *
19 * <p>Values for all {@link Thread}s can be reset from any {@link Thread}.
20 *
21 * @param <T> the type of the thread local variable
22 */
23 public class ThreadLocal<T> {
24
25 private final Map<Thread, T> contents = new ConcurrentHashMap<>();
26 private final Supplier<T> initialValueCreator;
27 private final Supplier<T> threadValueInitialiser =
28 new Supplier<T>() {
29 @Override
30 public T get() {
31 final T initialValue = initialValueCreator.get();
32 set(initialValue);
33 return initialValue;
34 }
35 };
36
37 /**
38 * @param initialValue the value this thread local will initially have for all {@link Thread}s.
39 * This should not be mutable, as it will be shared between all {@link Thread}s.
40 */
41 public ThreadLocal(final T initialValue) {
42 this(() -> requireNonNull(initialValue));
43 }
44
45 /**
46 * @param initialValueCreator a {@link Supplier} whose get method is called on a per {@link
47 * Thread} basis in order to establish the initial value for that {@link Thread}, allowing a
48 * different initial instance per {@link Thread}.
49 */
50 public ThreadLocal(final Supplier<T> initialValueCreator) {
51 this.initialValueCreator = requireNonNull(initialValueCreator);
52 }
53
54 /**
55 * @param value the new value for the calling {@link Thread} - does not affect the value for any
56 * other {@link Thread}.
57 */
58 public void set(final T value) {
59 contents.put(currentThread(), requireNonNull(value));
60 }
61
62 /**
63 * @return the value for the calling {@link Thread}, or the initial value if this has not been set
64 * or has been removed.
65 */
66 public T get() {
67 return ofNullable(contents.get(currentThread())).orElseGet(threadValueInitialiser);
68 }
69
70 /**
71 * Removes the value for the calling {@link Thread}. A subsequent call to {@link #get()} will
72 * return the initial value.
73 */
74 public void remove() {
75 contents.remove(currentThread());
76 }
77
78 /**
79 * Removes the values for ALL {@link Thread}s. Subsequent calls to {@link #get()} will return the
80 * initial value.
81 */
82 public void reset() {
83 contents.clear();
84 }
85
86 public Collection<T> allValues() {
87 return contents.values();
88 }
89 }