Java tutorial
/* * 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 io.netty.util.internal; import io.netty.util.concurrent.FastThreadLocal; import io.netty.util.concurrent.FastThreadLocalThread; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.IdentityHashMap; import java.util.Map; import java.util.WeakHashMap; /** * The internal data structure that stores the thread-local variables for Netty and all {@link FastThreadLocal}s. * Note that this class is for internal use only and is subject to change at any time. Use {@link FastThreadLocal} * unless you know what you are doing. */ public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap { private static final InternalLogger logger = InternalLoggerFactory.getInstance(InternalThreadLocalMap.class); private static final int DEFAULT_ARRAY_LIST_INITIAL_CAPACITY = 8; private static final int STRING_BUILDER_INITIAL_SIZE; private static final int STRING_BUILDER_MAX_SIZE; public static final Object UNSET = new Object(); private BitSet cleanerFlags; static { STRING_BUILDER_INITIAL_SIZE = SystemPropertyUtil.getInt("io.netty.threadLocalMap.stringBuilder.initialSize", 1024); logger.debug("-Dio.netty.threadLocalMap.stringBuilder.initialSize: {}", STRING_BUILDER_INITIAL_SIZE); STRING_BUILDER_MAX_SIZE = SystemPropertyUtil.getInt("io.netty.threadLocalMap.stringBuilder.maxSize", 1024 * 4); logger.debug("-Dio.netty.threadLocalMap.stringBuilder.maxSize: {}", STRING_BUILDER_MAX_SIZE); } public static InternalThreadLocalMap getIfSet() { Thread thread = Thread.currentThread(); if (thread instanceof FastThreadLocalThread) { return ((FastThreadLocalThread) thread).threadLocalMap(); } return slowThreadLocalMap.get(); } public static InternalThreadLocalMap get() { Thread thread = Thread.currentThread(); if (thread instanceof FastThreadLocalThread) { return fastGet((FastThreadLocalThread) thread); } else { return slowGet(); } } private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) { InternalThreadLocalMap threadLocalMap = thread.threadLocalMap(); if (threadLocalMap == null) { thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap()); } return threadLocalMap; } private static InternalThreadLocalMap slowGet() { ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap; InternalThreadLocalMap ret = slowThreadLocalMap.get(); if (ret == null) { ret = new InternalThreadLocalMap(); slowThreadLocalMap.set(ret); } return ret; } public static void remove() { Thread thread = Thread.currentThread(); if (thread instanceof FastThreadLocalThread) { ((FastThreadLocalThread) thread).setThreadLocalMap(null); } else { slowThreadLocalMap.remove(); } } public static void destroy() { slowThreadLocalMap.remove(); } public static int nextVariableIndex() { int index = nextIndex.getAndIncrement(); if (index < 0) { nextIndex.decrementAndGet(); throw new IllegalStateException("too many thread-local indexed variables"); } return index; } public static int lastVariableIndex() { return nextIndex.get() - 1; } // Cache line padding (must be public) // With CompressedOops enabled, an instance of this class should occupy at least 128 bytes. public long rp1, rp2, rp3, rp4, rp5, rp6, rp7, rp8, rp9; private InternalThreadLocalMap() { super(newIndexedVariableTable()); } private static Object[] newIndexedVariableTable() { Object[] array = new Object[32]; Arrays.fill(array, UNSET); return array; } public int size() { int count = 0; if (futureListenerStackDepth != 0) { count++; } if (localChannelReaderStackDepth != 0) { count++; } if (handlerSharableCache != null) { count++; } if (counterHashCode != null) { count++; } if (random != null) { count++; } if (typeParameterMatcherGetCache != null) { count++; } if (typeParameterMatcherFindCache != null) { count++; } if (stringBuilder != null) { count++; } if (charsetEncoderCache != null) { count++; } if (charsetDecoderCache != null) { count++; } if (arrayList != null) { count++; } for (Object o : indexedVariables) { if (o != UNSET) { count++; } } // We should subtract 1 from the count because the first element in 'indexedVariables' is reserved // by 'FastThreadLocal' to keep the list of 'FastThreadLocal's to remove on 'FastThreadLocal.removeAll()'. return count - 1; } public StringBuilder stringBuilder() { StringBuilder sb = stringBuilder; if (sb == null) { return stringBuilder = new StringBuilder(STRING_BUILDER_INITIAL_SIZE); } if (sb.capacity() > STRING_BUILDER_MAX_SIZE) { sb.setLength(STRING_BUILDER_INITIAL_SIZE); sb.trimToSize(); } sb.setLength(0); return sb; } public Map<Charset, CharsetEncoder> charsetEncoderCache() { Map<Charset, CharsetEncoder> cache = charsetEncoderCache; if (cache == null) { charsetEncoderCache = cache = new IdentityHashMap<Charset, CharsetEncoder>(); } return cache; } public Map<Charset, CharsetDecoder> charsetDecoderCache() { Map<Charset, CharsetDecoder> cache = charsetDecoderCache; if (cache == null) { charsetDecoderCache = cache = new IdentityHashMap<Charset, CharsetDecoder>(); } return cache; } public <E> ArrayList<E> arrayList() { return arrayList(DEFAULT_ARRAY_LIST_INITIAL_CAPACITY); } @SuppressWarnings("unchecked") public <E> ArrayList<E> arrayList(int minCapacity) { ArrayList<E> list = (ArrayList<E>) arrayList; if (list == null) { arrayList = new ArrayList<Object>(minCapacity); return (ArrayList<E>) arrayList; } list.clear(); list.ensureCapacity(minCapacity); return list; } public int futureListenerStackDepth() { return futureListenerStackDepth; } public void setFutureListenerStackDepth(int futureListenerStackDepth) { this.futureListenerStackDepth = futureListenerStackDepth; } public ThreadLocalRandom random() { ThreadLocalRandom r = random; if (r == null) { random = r = new ThreadLocalRandom(); } return r; } public Map<Class<?>, TypeParameterMatcher> typeParameterMatcherGetCache() { Map<Class<?>, TypeParameterMatcher> cache = typeParameterMatcherGetCache; if (cache == null) { typeParameterMatcherGetCache = cache = new IdentityHashMap<Class<?>, TypeParameterMatcher>(); } return cache; } public Map<Class<?>, Map<String, TypeParameterMatcher>> typeParameterMatcherFindCache() { Map<Class<?>, Map<String, TypeParameterMatcher>> cache = typeParameterMatcherFindCache; if (cache == null) { typeParameterMatcherFindCache = cache = new IdentityHashMap<Class<?>, Map<String, TypeParameterMatcher>>(); } return cache; } @Deprecated public IntegerHolder counterHashCode() { return counterHashCode; } @Deprecated public void setCounterHashCode(IntegerHolder counterHashCode) { this.counterHashCode = counterHashCode; } public Map<Class<?>, Boolean> handlerSharableCache() { Map<Class<?>, Boolean> cache = handlerSharableCache; if (cache == null) { // Start with small capacity to keep memory overhead as low as possible. handlerSharableCache = cache = new WeakHashMap<Class<?>, Boolean>(4); } return cache; } public int localChannelReaderStackDepth() { return localChannelReaderStackDepth; } public void setLocalChannelReaderStackDepth(int localChannelReaderStackDepth) { this.localChannelReaderStackDepth = localChannelReaderStackDepth; } public Object indexedVariable(int index) { Object[] lookup = indexedVariables; return index < lookup.length ? lookup[index] : UNSET; } /** * @return {@code true} if and only if a new thread-local variable has been created */ public boolean setIndexedVariable(int index, Object value) { Object[] lookup = indexedVariables; if (index < lookup.length) { Object oldValue = lookup[index]; lookup[index] = value; return oldValue == UNSET; } else { expandIndexedVariableTableAndSet(index, value); return true; } } private void expandIndexedVariableTableAndSet(int index, Object value) { Object[] oldArray = indexedVariables; final int oldCapacity = oldArray.length; int newCapacity = index; newCapacity |= newCapacity >>> 1; newCapacity |= newCapacity >>> 2; newCapacity |= newCapacity >>> 4; newCapacity |= newCapacity >>> 8; newCapacity |= newCapacity >>> 16; newCapacity++; Object[] newArray = Arrays.copyOf(oldArray, newCapacity); Arrays.fill(newArray, oldCapacity, newArray.length, UNSET); newArray[index] = value; indexedVariables = newArray; } public Object removeIndexedVariable(int index) { Object[] lookup = indexedVariables; if (index < lookup.length) { Object v = lookup[index]; lookup[index] = UNSET; return v; } else { return UNSET; } } public boolean isIndexedVariableSet(int index) { Object[] lookup = indexedVariables; return index < lookup.length && lookup[index] != UNSET; } public boolean isCleanerFlagSet(int index) { return cleanerFlags != null && cleanerFlags.get(index); } public void setCleanerFlag(int index) { if (cleanerFlags == null) { cleanerFlags = new BitSet(); } cleanerFlags.set(index); } }