All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.netty.util.concurrent.FastThreadLocal Maven / Gradle / Ivy

There is a newer version: 2.38.0
Show newest version
/*
 * Copyright 2014 The Netty Project
 *
 * The Netty Project licenses this file to you 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:
 *
 *   https://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 io.netty.util.concurrent;

import io.netty.util.internal.InternalThreadLocalMap;
import io.netty.util.internal.PlatformDependent;

import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Set;

import static io.netty.util.internal.InternalThreadLocalMap.VARIABLES_TO_REMOVE_INDEX;

/**
 * A special variant of {@link ThreadLocal} that yields higher access performance when accessed from a
 * {@link FastThreadLocalThread}.
 * 

* Internally, a {@link FastThreadLocal} uses a constant index in an array, instead of using hash code and hash table, * to look for a variable. Although seemingly very subtle, it yields slight performance advantage over using a hash * table, and it is useful when accessed frequently. *

* To take advantage of this thread-local variable, your thread must be a {@link FastThreadLocalThread} or its subtype. * By default, all threads created by {@link DefaultThreadFactory} are {@link FastThreadLocalThread} due to this reason. *

* Note that the fast path is only possible on threads that extend {@link FastThreadLocalThread}, because it requires * a special field to store the necessary state. An access by any other kind of thread falls back to a regular * {@link ThreadLocal}. *

* * @param the type of the thread-local variable * @see ThreadLocal */ public class FastThreadLocal { /** * Removes all {@link FastThreadLocal} variables bound to the current thread. This operation is useful when you * are in a container environment, and you don't want to leave the thread local variables in the threads you do not * manage. */ public static void removeAll() { InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet(); if (threadLocalMap == null) { return; } try { Object v = threadLocalMap.indexedVariable(VARIABLES_TO_REMOVE_INDEX); if (v != null && v != InternalThreadLocalMap.UNSET) { @SuppressWarnings("unchecked") Set> variablesToRemove = (Set>) v; FastThreadLocal[] variablesToRemoveArray = variablesToRemove.toArray(new FastThreadLocal[0]); for (FastThreadLocal tlv: variablesToRemoveArray) { tlv.remove(threadLocalMap); } } } finally { InternalThreadLocalMap.remove(); } } /** * Returns the number of thread local variables bound to the current thread. */ public static int size() { InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet(); if (threadLocalMap == null) { return 0; } else { return threadLocalMap.size(); } } /** * Destroys the data structure that keeps all {@link FastThreadLocal} variables accessed from * non-{@link FastThreadLocalThread}s. This operation is useful when you are in a container environment, and you * do not want to leave the thread local variables in the threads you do not manage. Call this method when your * application is being unloaded from the container. */ public static void destroy() { InternalThreadLocalMap.destroy(); } @SuppressWarnings("unchecked") private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal variable) { Object v = threadLocalMap.indexedVariable(VARIABLES_TO_REMOVE_INDEX); Set> variablesToRemove; if (v == InternalThreadLocalMap.UNSET || v == null) { variablesToRemove = Collections.newSetFromMap(new IdentityHashMap, Boolean>()); threadLocalMap.setIndexedVariable(VARIABLES_TO_REMOVE_INDEX, variablesToRemove); } else { variablesToRemove = (Set>) v; } variablesToRemove.add(variable); } private static void removeFromVariablesToRemove( InternalThreadLocalMap threadLocalMap, FastThreadLocal variable) { Object v = threadLocalMap.indexedVariable(VARIABLES_TO_REMOVE_INDEX); if (v == InternalThreadLocalMap.UNSET || v == null) { return; } @SuppressWarnings("unchecked") Set> variablesToRemove = (Set>) v; variablesToRemove.remove(variable); } private final int index; public FastThreadLocal() { index = InternalThreadLocalMap.nextVariableIndex(); } /** * Returns the current value for the current thread */ @SuppressWarnings("unchecked") public final V get() { InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get(); Object v = threadLocalMap.indexedVariable(index); if (v != InternalThreadLocalMap.UNSET) { return (V) v; } return initialize(threadLocalMap); } /** * Returns the current value for the current thread if it exists, {@code null} otherwise. */ @SuppressWarnings("unchecked") public final V getIfExists() { InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet(); if (threadLocalMap != null) { Object v = threadLocalMap.indexedVariable(index); if (v != InternalThreadLocalMap.UNSET) { return (V) v; } } return null; } /** * Returns the current value for the specified thread local map. * The specified thread local map must be for the current thread. */ @SuppressWarnings("unchecked") public final V get(InternalThreadLocalMap threadLocalMap) { Object v = threadLocalMap.indexedVariable(index); if (v != InternalThreadLocalMap.UNSET) { return (V) v; } return initialize(threadLocalMap); } private V initialize(InternalThreadLocalMap threadLocalMap) { V v = null; try { v = initialValue(); if (v == InternalThreadLocalMap.UNSET) { throw new IllegalArgumentException("InternalThreadLocalMap.UNSET can not be initial value."); } } catch (Exception e) { PlatformDependent.throwException(e); } threadLocalMap.setIndexedVariable(index, v); addToVariablesToRemove(threadLocalMap, this); return v; } /** * Set the value for the current thread. */ public final void set(V value) { if (value != InternalThreadLocalMap.UNSET) { InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get(); setKnownNotUnset(threadLocalMap, value); } else { remove(); } } /** * Set the value for the specified thread local map. The specified thread local map must be for the current thread. */ public final void set(InternalThreadLocalMap threadLocalMap, V value) { if (value != InternalThreadLocalMap.UNSET) { setKnownNotUnset(threadLocalMap, value); } else { remove(threadLocalMap); } } /** * @see InternalThreadLocalMap#setIndexedVariable(int, Object). */ private void setKnownNotUnset(InternalThreadLocalMap threadLocalMap, V value) { if (threadLocalMap.setIndexedVariable(index, value)) { addToVariablesToRemove(threadLocalMap, this); } } /** * Returns {@code true} if and only if this thread-local variable is set. */ public final boolean isSet() { return isSet(InternalThreadLocalMap.getIfSet()); } /** * Returns {@code true} if and only if this thread-local variable is set. * The specified thread local map must be for the current thread. */ public final boolean isSet(InternalThreadLocalMap threadLocalMap) { return threadLocalMap != null && threadLocalMap.isIndexedVariableSet(index); } /** * Sets the value to uninitialized for the specified thread local map. * After this, any subsequent call to get() will trigger a new call to initialValue(). */ public final void remove() { remove(InternalThreadLocalMap.getIfSet()); } /** * Sets the value to uninitialized for the specified thread local map. * After this, any subsequent call to get() will trigger a new call to initialValue(). * The specified thread local map must be for the current thread. */ @SuppressWarnings("unchecked") public final void remove(InternalThreadLocalMap threadLocalMap) { if (threadLocalMap == null) { return; } Object v = threadLocalMap.removeIndexedVariable(index); if (v != InternalThreadLocalMap.UNSET) { removeFromVariablesToRemove(threadLocalMap, this); try { onRemoval((V) v); } catch (Exception e) { PlatformDependent.throwException(e); } } } /** * Returns the initial value for this thread-local variable. */ protected V initialValue() throws Exception { return null; } /** * Invoked when this thread local variable is removed by {@link #remove()}. Be aware that {@link #remove()} * is not guaranteed to be called when the `Thread` completes which means you can not depend on this for * cleanup of the resources in the case of `Thread` completion. */ protected void onRemoval(@SuppressWarnings("UnusedParameters") V value) throws Exception { } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy