View Javadoc
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  }