/* * Copyright (C) 2008 The Android Open Source Project * * 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 java.lang; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.concurrent.atomic.AtomicInteger; /** * Implements a thread-local storage, that is, a variable for which each thread * has its own value. All threads share the same {@code ThreadLocal} object, * but each sees a different value when accessing it, and changes made by one * thread do not affect the other threads. The implementation supports * {@code null} values. * * @see java.lang.Thread * @author Bob Lee */ public class ThreadLocal { /* Thanks to Josh Bloch and Doug Lea for code reviews and impl advice. */ /** * Creates a new thread-local variable. */ public ThreadLocal() {} /** * Returns the value of this variable for the current thread. If an entry * doesn't yet exist for this variable on this thread, this method will * create an entry, populating the value with the result of * {@link #initialValue()}. * * @return the current value of the variable for the calling thread. */ @SuppressWarnings("unchecked") public T get() { // Optimized for the fast path. Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values != null) { Object[] table = values.table; int index = hash & values.mask; if (this.reference == table[index]) { return (T) table[index + 1]; } } else { values = initializeValues(currentThread); } return (T) values.getAfterMiss(this); } /** * Provides the initial value of this variable for the current thread. * The default implementation returns {@code null}. * * @return the initial value of the variable. */ protected T initialValue() { return null; } /** * Sets the value of this variable for the current thread. If set to * {@code null}, the value will be set to null and the underlying entry will * still be present. * * @param value the new value of the variable for the caller thread. */ public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value); } /** * Removes the entry for this variable in the current thread. If this call * is followed by a {@link #get()} before a {@link #set}, * {@code #get()} will call {@link #initialValue()} and create a new * entry with the resulting value. * * @since 1.5 */ public void remove() { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values != null) { values.remove(this); } } /** * Creates Values instance for this thread and variable type. */ Values initializeValues(Thread current) { return current.localValues = new Values(); } /** * Gets Values instance for this thread and variable type. */ Values values(Thread current) { return current.localValues; } /** Weak reference to this thread local instance. */ private final Reference> reference = new WeakReference>(this); /** Hash counter. */ private static AtomicInteger hashCounter = new AtomicInteger(0); /** * Internal hash. We deliberately don't bother with #hashCode(). * Hashes must be even. This ensures that the result of * (hash & (table.length - 1)) points to a key and not a value. * * We increment by Doug Lea's Magic Number(TM) (*2 since keys are in * every other bucket) to help prevent clustering. */ private final int hash = hashCounter.getAndAdd(0x61c88647 * 2); /** * Per-thread map of ThreadLocal instances to values. */ static class Values { /** * Size must always be a power of 2. */ private static final int INITIAL_SIZE = 16; /** * Placeholder for deleted entries. */ private static final Object TOMBSTONE = new Object(); /** * Map entries. Contains alternating keys (ThreadLocal) and values. * The length is always a power of 2. */ private Object[] table; /** Used to turn hashes into indices. */ private int mask; /** Number of live entries. */ private int size; /** Number of tombstones. */ private int tombstones; /** Maximum number of live entries and tombstones. */ private int maximumLoad; /** Points to the next cell to clean up. */ private int clean; /** * Constructs a new, empty instance. */ Values() { initializeTable(INITIAL_SIZE); this.size = 0; this.tombstones = 0; } /** * Used for InheritableThreadLocals. */ Values(Values fromParent) { this.table = fromParent.table.clone(); this.mask = fromParent.mask; this.size = fromParent.size; this.tombstones = fromParent.tombstones; this.maximumLoad = fromParent.maximumLoad; this.clean = fromParent.clean; inheritValues(fromParent); } /** * Inherits values from a parent thread. */ @SuppressWarnings({"unchecked"}) private void inheritValues(Values fromParent) { // Transfer values from parent to child thread. Object[] table = this.table; for (int i = table.length - 2; i >= 0; i -= 2) { Object k = table[i]; if (k == null || k == TOMBSTONE) { // Skip this entry. continue; } // The table can only contain null, tombstones and references. Reference> reference = (Reference>) k; // Raw type enables us to pass in an Object below. InheritableThreadLocal key = reference.get(); if (key != null) { // Replace value with filtered value. // We should just let exceptions bubble out and tank // the thread creation table[i + 1] = key.childValue(fromParent.table[i + 1]); } else { // The key was reclaimed. table[i] = TOMBSTONE; table[i + 1] = null; fromParent.table[i] = TOMBSTONE; fromParent.table[i + 1] = null; tombstones++; fromParent.tombstones++; size--; fromParent.size--; } } } /** * Creates a new, empty table with the given capacity. */ private void initializeTable(int capacity) { this.table = new Object[capacity * 2]; this.mask = table.length - 1; this.clean = 0; this.maximumLoad = capacity * 2 / 3; // 2/3 } /** * Cleans up after garbage-collected thread locals. */ private void cleanUp() { if (rehash()) { // If we rehashed, we needn't clean up (clean up happens as // a side effect). return; } if (size == 0) { // No live entries == nothing to clean. return; } // Clean log(table.length) entries picking up where we left off // last time. int index = clean; Object[] table = this.table; for (int counter = table.length; counter > 0; counter >>= 1, index = next(index)) { Object k = table[index]; if (k == TOMBSTONE || k == null) { continue; // on to next entry } // The table can only contain null, tombstones and references. @SuppressWarnings("unchecked") Reference> reference = (Reference>) k; if (reference.get() == null) { // This thread local was reclaimed by the garbage collector. table[index] = TOMBSTONE; table[index + 1] = null; tombstones++; size--; } } // Point cursor to next index. clean = index; } /** * Rehashes the table, expanding or contracting it as necessary. * Gets rid of tombstones. Returns true if a rehash occurred. * We must rehash every time we fill a null slot; we depend on the * presence of null slots to end searches (otherwise, we'll infinitely * loop). */ private boolean rehash() { if (tombstones + size < maximumLoad) { return false; } int capacity = table.length >> 1; // Default to the same capacity. This will create a table of the // same size and move over the live entries, analogous to a // garbage collection. This should only happen if you churn a // bunch of thread local garbage (removing and reinserting // the same thread locals over and over will overwrite tombstones // and not fill up the table). int newCapacity = capacity; if (size > (capacity >> 1)) { // More than 1/2 filled w/ live entries. // Double size. newCapacity = capacity * 2; } Object[] oldTable = this.table; // Allocate new table. initializeTable(newCapacity); // We won't have any tombstones after this. this.tombstones = 0; // If we have no live entries, we can quit here. if (size == 0) { return true; } // Move over entries. for (int i = oldTable.length - 2; i >= 0; i -= 2) { Object k = oldTable[i]; if (k == null || k == TOMBSTONE) { // Skip this entry. continue; } // The table can only contain null, tombstones and references. @SuppressWarnings("unchecked") Reference> reference = (Reference>) k; ThreadLocal key = reference.get(); if (key != null) { // Entry is still live. Move it over. add(key, oldTable[i + 1]); } else { // The key was reclaimed. size--; } } return true; } /** * Adds an entry during rehashing. Compared to put(), this method * doesn't have to clean up, check for existing entries, account for * tombstones, etc. */ void add(ThreadLocal key, Object value) { for (int index = key.hash & mask;; index = next(index)) { Object k = table[index]; if (k == null) { table[index] = key.reference; table[index + 1] = value; return; } } } /** * Sets entry for given ThreadLocal to given value, creating an * entry if necessary. */ void put(ThreadLocal key, Object value) { cleanUp(); // Keep track of first tombstone. That's where we want to go back // and add an entry if necessary. int firstTombstone = -1; for (int index = key.hash & mask;; index = next(index)) { Object k = table[index]; if (k == key.reference) { // Replace existing entry. table[index + 1] = value; return; } if (k == null) { if (firstTombstone == -1) { // Fill in null slot. table[index] = key.reference; table[index + 1] = value; size++; return; } // Go back and replace first tombstone. table[firstTombstone] = key.reference; table[firstTombstone + 1] = value; tombstones--; size++; return; } // Remember first tombstone. if (firstTombstone == -1 && k == TOMBSTONE) { firstTombstone = index; } } } /** * Gets value for given ThreadLocal after not finding it in the first * slot. */ Object getAfterMiss(ThreadLocal key) { Object[] table = this.table; int index = key.hash & mask; // If the first slot is empty, the search is over. if (table[index] == null) { Object value = key.initialValue(); // If the table is still the same and the slot is still empty... if (this.table == table && table[index] == null) { table[index] = key.reference; table[index + 1] = value; size++; cleanUp(); return value; } // The table changed during initialValue(). put(key, value); return value; } // Keep track of first tombstone. That's where we want to go back // and add an entry if necessary. int firstTombstone = -1; // Continue search. for (index = next(index);; index = next(index)) { Object reference = table[index]; if (reference == key.reference) { return table[index + 1]; } // If no entry was found... if (reference == null) { Object value = key.initialValue(); // If the table is still the same... if (this.table == table) { // If we passed a tombstone and that slot still // contains a tombstone... if (firstTombstone > -1 && table[firstTombstone] == TOMBSTONE) { table[firstTombstone] = key.reference; table[firstTombstone + 1] = value; tombstones--; size++; // No need to clean up here. We aren't filling // in a null slot. return value; } // If this slot is still empty... if (table[index] == null) { table[index] = key.reference; table[index + 1] = value; size++; cleanUp(); return value; } } // The table changed during initialValue(). put(key, value); return value; } if (firstTombstone == -1 && reference == TOMBSTONE) { // Keep track of this tombstone so we can overwrite it. firstTombstone = index; } } } /** * Removes entry for the given ThreadLocal. */ void remove(ThreadLocal key) { cleanUp(); for (int index = key.hash & mask;; index = next(index)) { Object reference = table[index]; if (reference == key.reference) { // Success! table[index] = TOMBSTONE; table[index + 1] = null; tombstones++; size--; return; } if (reference == null) { // No entry found. return; } } } /** * Gets the next index. If we're at the end of the table, we wrap back * around to 0. */ private int next(int index) { return (index + 2) & mask; } } }