001
014
015 package com.liferay.portal.kernel.util;
016
017 import java.util.HashMap;
018 import java.util.HashSet;
019 import java.util.Map;
020 import java.util.Set;
021 import java.util.concurrent.atomic.AtomicInteger;
022
023
026 public class CentralizedThreadLocal<T> extends ThreadLocal<T> {
027
028 public static void clearLongLivedThreadLocals() {
029 _longLivedThreadLocals.remove();
030 }
031
032 public static void clearShortLivedThreadLocals() {
033 _shortLivedThreadLocals.remove();
034 }
035
036 public static Map<CentralizedThreadLocal<?>, Object>
037 getLongLivedThreadLocals() {
038
039 return _toMap(_longLivedThreadLocals.get());
040 }
041
042 public static Map<CentralizedThreadLocal<?>, Object>
043 getShortLivedThreadLocals() {
044
045 return _toMap(_shortLivedThreadLocals.get());
046 }
047
048 public static void setThreadLocals(
049 Map<CentralizedThreadLocal<?>, Object> longLivedThreadLocals,
050 Map<CentralizedThreadLocal<?>, Object> shortLivedThreadLocals) {
051
052 ThreadLocalMap threadLocalMap = _longLivedThreadLocals.get();
053
054 for (Map.Entry<CentralizedThreadLocal<?>, Object> entry :
055 longLivedThreadLocals.entrySet()) {
056
057 threadLocalMap.putEntry(entry.getKey(), entry.getValue());
058 }
059
060 threadLocalMap = _shortLivedThreadLocals.get();
061
062 for (Map.Entry<CentralizedThreadLocal<?>, Object> entry :
063 shortLivedThreadLocals.entrySet()) {
064
065 threadLocalMap.putEntry(entry.getKey(), entry.getValue());
066 }
067 }
068
069 public CentralizedThreadLocal(boolean shortLived) {
070 _shortLived = shortLived;
071
072 if (shortLived) {
073 _hashCode = _shortLivedNextHasCode.getAndAdd(_HASH_INCREMENT);
074 }
075 else {
076 _hashCode = _longLivedNextHasCode.getAndAdd(_HASH_INCREMENT);
077 }
078 }
079
080 @Override
081 public T get() {
082 ThreadLocalMap threadLocalMap = _getThreadLocalMap();
083
084 Entry entry = threadLocalMap.getEntry(this);
085
086 if (entry == null) {
087 T value = initialValue();
088
089 threadLocalMap.putEntry(this, value);
090
091 return value;
092 }
093 else {
094 return (T)entry._value;
095 }
096 }
097
098 @Override
099 public int hashCode() {
100 return _hashCode;
101 }
102
103 @Override
104 public void remove() {
105 ThreadLocalMap threadLocalMap = _getThreadLocalMap();
106
107 threadLocalMap.removeEntry(this);
108 }
109
110 @Override
111 public void set(T value) {
112 ThreadLocalMap threadLocalMap = _getThreadLocalMap();
113
114 threadLocalMap.putEntry(this, value);
115 }
116
117 protected T copy(T value) {
118 if (value != null) {
119 Class<?> clazz = value.getClass();
120
121 if (_immutableTypes.contains(clazz)) {
122 return value;
123 }
124 }
125
126 return null;
127 }
128
129 private static Map<CentralizedThreadLocal<?>, Object> _toMap(
130 ThreadLocalMap threadLocalMap) {
131
132 Map<CentralizedThreadLocal<?>, Object> map =
133 new HashMap<CentralizedThreadLocal<?>, Object>(
134 threadLocalMap._table.length);
135
136 for (Entry entry : threadLocalMap._table) {
137 if (entry != null) {
138 CentralizedThreadLocal<Object> centralizedThreadLocal =
139 (CentralizedThreadLocal<Object>)entry._key;
140
141 Object value = centralizedThreadLocal.copy(entry._value);
142
143 if (value != null) {
144 map.put(centralizedThreadLocal, value);
145 }
146 }
147 }
148
149 return map;
150 }
151
152 private ThreadLocalMap _getThreadLocalMap() {
153 if (_shortLived) {
154 return _shortLivedThreadLocals.get();
155 }
156 else {
157 return _longLivedThreadLocals.get();
158 }
159 }
160
161 private static final int _HASH_INCREMENT = 0x61c88647;
162
163 private static final Set<Class<?>> _immutableTypes =
164 new HashSet<Class<?>>();
165 private static final AtomicInteger _longLivedNextHasCode =
166 new AtomicInteger();
167 private static final ThreadLocal<ThreadLocalMap> _longLivedThreadLocals =
168 new ThreadLocalMapThreadLocal();
169 private static final AtomicInteger _shortLivedNextHasCode =
170 new AtomicInteger();
171 private static final ThreadLocal<ThreadLocalMap> _shortLivedThreadLocals =
172 new ThreadLocalMapThreadLocal();
173
174 static {
175 _immutableTypes.add(Boolean.class);
176 _immutableTypes.add(Byte.class);
177 _immutableTypes.add(Character.class);
178 _immutableTypes.add(Short.class);
179 _immutableTypes.add(Integer.class);
180 _immutableTypes.add(Long.class);
181 _immutableTypes.add(Float.class);
182 _immutableTypes.add(Double.class);
183 _immutableTypes.add(String.class);
184 }
185
186 private final int _hashCode;
187 private final boolean _shortLived;
188
189 private static class Entry {
190
191 public Entry(CentralizedThreadLocal<?> key, Object value, Entry next) {
192 _key = key;
193 _value = value;
194 _next = next;
195 }
196
197 private CentralizedThreadLocal<?> _key;
198 private Entry _next;
199 private Object _value;
200
201 }
202
203 private static class ThreadLocalMap {
204
205 public void expand(int newCapacity) {
206 if (_table.length == _MAXIMUM_CAPACITY) {
207 _threshold = Integer.MAX_VALUE;
208
209 return;
210 }
211
212 Entry[] newTable = new Entry[newCapacity];
213
214 for (int i = 0; i < _table.length; i++) {
215 Entry entry = _table[i];
216
217 if (entry == null) {
218 continue;
219 }
220
221 _table[i] = null;
222
223 do {
224 Entry nextEntry = entry._next;
225
226 int index = entry._key._hashCode & (newCapacity - 1);
227
228 entry._next = newTable[index];
229
230 newTable[index] = entry;
231
232 entry = nextEntry;
233 }
234 while (entry != null);
235 }
236
237 _table = newTable;
238
239 _threshold = newCapacity * 2 / 3;
240 }
241
242 public Entry getEntry(CentralizedThreadLocal<?> key) {
243 int index = key._hashCode & (_table.length - 1);
244
245 Entry entry = _table[index];
246
247 if (entry == null) {
248 return null;
249 }
250
251 if (entry._key == key) {
252 return entry;
253 }
254
255 while ((entry = entry._next) != null) {
256 if (entry._key == key) {
257 return entry;
258 }
259 }
260
261 return null;
262 }
263
264 public void putEntry(CentralizedThreadLocal<?> key, Object value) {
265 int index = key._hashCode & (_table.length - 1);
266
267 for (Entry entry = _table[index]; entry != null;
268 entry = entry._next) {
269
270 if (entry._key == key) {
271 entry._value = value;
272
273 return;
274 }
275 }
276
277 _table[index] = new Entry(key, value, _table[index]);
278
279 if (_size++ >= _threshold) {
280 expand(2 * _table.length);
281 }
282 }
283
284 public void removeEntry(CentralizedThreadLocal<?> key) {
285 int index = key._hashCode & (_table.length - 1);
286
287 Entry previousEntry = null;
288
289 Entry entry = _table[index];
290
291 while (entry != null) {
292 Entry nextEntry = entry._next;
293
294 if (entry._key == key) {
295 _size--;
296
297 if (previousEntry == null) {
298 _table[index] = nextEntry;
299 }
300 else {
301 previousEntry._next = nextEntry;
302 }
303
304 return;
305 }
306
307 previousEntry = entry;
308 entry = nextEntry;
309 }
310 }
311
312 private static final int _INITIAL_CAPACITY = 16;
313
314 private static final int _MAXIMUM_CAPACITY = 1 << 30;
315
316 private int _size;
317 private Entry[] _table = new Entry[_INITIAL_CAPACITY];
318 private int _threshold = _INITIAL_CAPACITY * 2 / 3;
319
320 }
321
322 private static class ThreadLocalMapThreadLocal
323 extends ThreadLocal<ThreadLocalMap> {
324
325 @Override
326 protected ThreadLocalMap initialValue() {
327 return new ThreadLocalMap();
328 }
329
330 }
331
332 }