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

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

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
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