| /* |
| * 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: |
| * |
| * 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 com.alibaba.dubbo.common.threadlocal; |
| |
| import java.util.Collections; |
| import java.util.IdentityHashMap; |
| import java.util.Set; |
| |
| /** |
| * InternalThreadLocal |
| * A special variant of {@link ThreadLocal} that yields higher access performance when accessed from a |
| * {@link InternalThread}. |
| * <p></p> |
| * Internally, a {@link InternalThread} 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. |
| * <p></p> |
| * This design is learning from {@see io.netty.util.concurrent.FastThreadLocal} which is in Netty. |
| */ |
| public class InternalThreadLocal<V> { |
| |
| private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex(); |
| |
| private final int index; |
| |
| public InternalThreadLocal() { |
| index = InternalThreadLocalMap.nextVariableIndex(); |
| } |
| |
| /** |
| * Removes all {@link InternalThreadLocal} 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. |
| */ |
| @SuppressWarnings("unchecked") |
| public static void removeAll() { |
| InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet(); |
| if (threadLocalMap == null) { |
| return; |
| } |
| |
| try { |
| Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex); |
| if (v != null && v != InternalThreadLocalMap.UNSET) { |
| Set<InternalThreadLocal<?>> variablesToRemove = (Set<InternalThreadLocal<?>>) v; |
| InternalThreadLocal<?>[] variablesToRemoveArray = |
| variablesToRemove.toArray(new InternalThreadLocal[variablesToRemove.size()]); |
| for (InternalThreadLocal<?> 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(); |
| } |
| } |
| |
| public static void destroy() { |
| InternalThreadLocalMap.destroy(); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, InternalThreadLocal<?> variable) { |
| Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex); |
| Set<InternalThreadLocal<?>> variablesToRemove; |
| if (v == InternalThreadLocalMap.UNSET || v == null) { |
| variablesToRemove = Collections.newSetFromMap(new IdentityHashMap<InternalThreadLocal<?>, Boolean>()); |
| threadLocalMap.setIndexedVariable(variablesToRemoveIndex, variablesToRemove); |
| } else { |
| variablesToRemove = (Set<InternalThreadLocal<?>>) v; |
| } |
| |
| variablesToRemove.add(variable); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private static void removeFromVariablesToRemove(InternalThreadLocalMap threadLocalMap, InternalThreadLocal<?> variable) { |
| |
| Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex); |
| |
| if (v == InternalThreadLocalMap.UNSET || v == null) { |
| return; |
| } |
| |
| Set<InternalThreadLocal<?>> variablesToRemove = (Set<InternalThreadLocal<?>>) v; |
| variablesToRemove.remove(variable); |
| } |
| |
| /** |
| * 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); |
| } |
| |
| private V initialize(InternalThreadLocalMap threadLocalMap) { |
| V v = null; |
| try { |
| v = initialValue(); |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| |
| threadLocalMap.setIndexedVariable(index, v); |
| addToVariablesToRemove(threadLocalMap, this); |
| return v; |
| } |
| |
| /** |
| * Sets the value for the current thread. |
| */ |
| public final void set(V value) { |
| if (value == null || value == InternalThreadLocalMap.UNSET) { |
| remove(); |
| } else { |
| InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get(); |
| if (threadLocalMap.setIndexedVariable(index, value)) { |
| addToVariablesToRemove(threadLocalMap, this); |
| } |
| } |
| } |
| |
| /** |
| * Sets the value to uninitialized; a proceeding call to get() will trigger a call to initialValue(). |
| */ |
| @SuppressWarnings("unchecked") |
| public final void remove() { |
| remove(InternalThreadLocalMap.getIfSet()); |
| } |
| |
| /** |
| * Sets the value to uninitialized for the specified thread local map; |
| * a proceeding call to get() will trigger a 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); |
| removeFromVariablesToRemove(threadLocalMap, this); |
| |
| if (v != InternalThreadLocalMap.UNSET) { |
| try { |
| onRemoval((V) v); |
| } catch (Exception e) { |
| throw new RuntimeException(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()}. |
| */ |
| protected void onRemoval(@SuppressWarnings("unused") V value) throws Exception { |
| } |
| } |