< prev index next >

src/java.base/share/classes/java/lang/ThreadLocal.java

Print this page
*** 22,17 ***
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  package java.lang;
- import jdk.internal.misc.TerminatingThreadLocal;
  
  import java.lang.ref.WeakReference;
  import java.util.Objects;
  import java.util.concurrent.atomic.AtomicInteger;
  import java.util.function.Supplier;
  
  /**
   * This class provides thread-local variables.  These variables differ from
   * their normal counterparts in that each thread that accesses one (via its
   * {@code get} or {@code set} method) has its own, independently initialized
   * copy of the variable.  {@code ThreadLocal} instances are typically private
--- 22,18 ---
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  package java.lang;
  
  import java.lang.ref.WeakReference;
  import java.util.Objects;
  import java.util.concurrent.atomic.AtomicInteger;
  import java.util.function.Supplier;
  
+ import jdk.internal.misc.TerminatingThreadLocal;
+ 
  /**
   * This class provides thread-local variables.  These variables differ from
   * their normal counterparts in that each thread that accesses one (via its
   * {@code get} or {@code set} method) has its own, independently initialized
   * copy of the variable.  {@code ThreadLocal} instances are typically private

*** 101,11 ***
      private static final int HASH_INCREMENT = 0x61c88647;
  
      /**
       * Returns the next hash code.
       */
!     private static int nextHashCode() {
          return nextHashCode.getAndAdd(HASH_INCREMENT);
      }
  
      /**
       * Returns the current thread's "initial value" for this
--- 102,11 ---
      private static final int HASH_INCREMENT = 0x61c88647;
  
      /**
       * Returns the next hash code.
       */
!     static int nextHashCode() {
          return nextHashCode.getAndAdd(HASH_INCREMENT);
      }
  
      /**
       * Returns the current thread's "initial value" for this

*** 153,25 ***
      /**
       * Returns the value in the current thread's copy of this
       * thread-local variable.  If the variable has no value for the
       * current thread, it is first initialized to the value returned
       * by an invocation of the {@link #initialValue} method.
       *
       * @return the current thread's value of this thread-local
       */
      public T get() {
!         Thread t = Thread.currentThread();
          ThreadLocalMap map = getMap(t);
!         if (map != null) {
              ThreadLocalMap.Entry e = map.getEntry(this);
              if (e != null) {
                  @SuppressWarnings("unchecked")
                  T result = (T)e.value;
                  return result;
              }
          }
!         return setInitialValue();
      }
  
      /**
       * Returns {@code true} if there is a value in the current thread's copy of
       * this thread-local variable, even if that values is {@code null}.
--- 154,40 ---
      /**
       * Returns the value in the current thread's copy of this
       * thread-local variable.  If the variable has no value for the
       * current thread, it is first initialized to the value returned
       * by an invocation of the {@link #initialValue} method.
+      * If the current thread does not support thread locals then
+      * this method returns its {@link #initialValue} (or {@code null}
+      * if the {@code initialValue} method is not overridden).
       *
       * @return the current thread's value of this thread-local
+      * @see Thread.Builder#allowSetThreadLocals(boolean)
       */
      public T get() {
!         return get(Thread.currentThread());
+     }
+ 
+     /**
+      * Returns the value in the current carrier thread's copy of this
+      * thread-local variable.
+      */
+     T getCarrierThreadLocal() {
+         return get(Thread.currentCarrierThread());
+     }
+ 
+     private T get(Thread t) {
          ThreadLocalMap map = getMap(t);
!         if (map != null && map != ThreadLocalMap.NOT_SUPPORTED) {
              ThreadLocalMap.Entry e = map.getEntry(this);
              if (e != null) {
                  @SuppressWarnings("unchecked")
                  T result = (T)e.value;
                  return result;
              }
          }
!         return setInitialValue(t);
      }
  
      /**
       * Returns {@code true} if there is a value in the current thread's copy of
       * this thread-local variable, even if that values is {@code null}.

*** 180,23 ***
       *         thread-local variable; {@code false} if not
       */
      boolean isPresent() {
          Thread t = Thread.currentThread();
          ThreadLocalMap map = getMap(t);
!         return map != null && map.getEntry(this) != null;
      }
  
      /**
       * Variant of set() to establish initialValue. Used instead
       * of set() in case user has overridden the set() method.
       *
       * @return the initial value
       */
!     private T setInitialValue() {
          T value = initialValue();
-         Thread t = Thread.currentThread();
          ThreadLocalMap map = getMap(t);
          if (map != null) {
              map.set(this, value);
          } else {
              createMap(t, value);
          }
--- 196,29 ---
       *         thread-local variable; {@code false} if not
       */
      boolean isPresent() {
          Thread t = Thread.currentThread();
          ThreadLocalMap map = getMap(t);
!         if (map != null && map != ThreadLocalMap.NOT_SUPPORTED) {
+             return map.getEntry(this) != null;
+         } else {
+             return false;
+         }
      }
  
      /**
       * Variant of set() to establish initialValue. Used instead
       * of set() in case user has overridden the set() method.
       *
       * @return the initial value
       */
!     private T setInitialValue(Thread t) {
          T value = initialValue();
          ThreadLocalMap map = getMap(t);
+         if (map == ThreadLocalMap.NOT_SUPPORTED) {
+             return value;
+         }
          if (map != null) {
              map.set(this, value);
          } else {
              createMap(t, value);
          }

*** 212,14 ***
       * override this method, relying solely on the {@link #initialValue}
       * method to set the values of thread-locals.
       *
       * @param value the value to be stored in the current thread's copy of
       *        this thread-local.
       */
      public void set(T value) {
!         Thread t = Thread.currentThread();
          ThreadLocalMap map = getMap(t);
          if (map != null) {
              map.set(this, value);
          } else {
              createMap(t, value);
          }
--- 234,29 ---
       * override this method, relying solely on the {@link #initialValue}
       * method to set the values of thread-locals.
       *
       * @param value the value to be stored in the current thread's copy of
       *        this thread-local.
+      *
+      * @throws UnsupportedOperationException if the current thread is not
+      *         allowed to set its copy of thread-local variables
+      *
+      * @see Thread.Builder#allowSetThreadLocals(boolean)
       */
      public void set(T value) {
!         set(Thread.currentThread(), value);
+     }
+ 
+     void setCarrierThreadLocal(T value) {
+         set(Thread.currentCarrierThread(), value);
+     }
+ 
+     private void set(Thread t, T value) {
          ThreadLocalMap map = getMap(t);
+         if (map == ThreadLocalMap.NOT_SUPPORTED) {
+             throw new UnsupportedOperationException();
+         }
          if (map != null) {
              map.set(this, value);
          } else {
              createMap(t, value);
          }

*** 236,11 ***
       *
       * @since 1.5
       */
       public void remove() {
           ThreadLocalMap m = getMap(Thread.currentThread());
!          if (m != null) {
               m.remove(this);
           }
       }
  
      /**
--- 273,11 ---
       *
       * @since 1.5
       */
       public void remove() {
           ThreadLocalMap m = getMap(Thread.currentThread());
!          if (m != null && m != ThreadLocalMap.NOT_SUPPORTED) {
               m.remove(this);
           }
       }
  
      /**

*** 334,10 ***
--- 371,12 ---
                  super(k);
                  value = v;
              }
          }
  
+         static final ThreadLocalMap NOT_SUPPORTED = new ThreadLocalMap();
+ 
          /**
           * The initial capacity -- MUST be a power of two.
           */
          private static final int INITIAL_CAPACITY = 16;
  

*** 376,10 ***
--- 415,13 ---
           */
          private static int prevIndex(int i, int len) {
              return ((i - 1 >= 0) ? i - 1 : len - 1);
          }
  
+         ThreadLocalMap() {
+         }
+ 
          /**
           * Construct a new map initially containing (firstKey, firstValue).
           * ThreadLocalMaps are constructed lazily, so we only create
           * one when we have at least one entry to put in it.
           */

*** 418,10 ***
--- 460,17 ---
                      }
                  }
              }
          }
  
+         /**
+          * Returns the number of elements in the map.
+          */
+         int size() {
+             return size;
+         }
+ 
          /**
           * Get the entry associated with key.  This method
           * itself handles only the fast path: a direct hit of existing
           * key. It otherwise relays to getEntryAfterMiss.  This is
           * designed to maximize performance for direct hits, in part
< prev index next >