org.apache.dubbo.common.threadlocal.InternalThreadLocal Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dubbo Show documentation
Show all versions of dubbo Show documentation
The all in one project of dubbo
/*
* 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 org.apache.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}.
*
* 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.
*
* This design is learning from {@see io.netty.util.concurrent.FastThreadLocal} which is in Netty.
*/
public class InternalThreadLocal extends ThreadLocal {
private static final int VARIABLES_TO_REMOVE_INDEX = 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(VARIABLES_TO_REMOVE_INDEX);
if (v != null && v != InternalThreadLocalMap.UNSET) {
Set> variablesToRemove = (Set>) v;
InternalThreadLocal[] variablesToRemoveArray =
variablesToRemove.toArray(new InternalThreadLocal[0]);
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(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);
}
@SuppressWarnings("unchecked")
private static void removeFromVariablesToRemove(InternalThreadLocalMap threadLocalMap, InternalThreadLocal variable) {
Object v = threadLocalMap.indexedVariable(VARIABLES_TO_REMOVE_INDEX);
if (v == InternalThreadLocalMap.UNSET || v == null) {
return;
}
Set> variablesToRemove = (Set>) v;
variablesToRemove.remove(variable);
}
/**
* Returns the current value for the current thread
*/
@SuppressWarnings("unchecked")
@Override
public final V get() {
InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
Object v = threadLocalMap.indexedVariable(index);
if (v != InternalThreadLocalMap.UNSET) {
return (V) v;
}
return initialize(threadLocalMap);
}
public final V getWithoutInitialize() {
InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
Object v = threadLocalMap.indexedVariable(index);
if (v != InternalThreadLocalMap.UNSET) {
return (V) v;
}
return null;
}
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.
*/
@Override
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")
@Override
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.
*/
@Override
protected V initialValue() {
return null;
}
/**
* Invoked when this thread local variable is removed by {@link #remove()}.
*/
protected void onRemoval(@SuppressWarnings("unused") V value) throws Exception {
}
}