* This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* {@code get} or {@code set} method) has its own, independently initialized
* copy of the variable. {@code ThreadLocal} instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).
Класс ThreadLocal используется для предоставлениялокальные переменные внутри потока. К этому типу переменных можно получить доступ в многопоточной среде (через метод get или set).Убедитесь, что переменные в каждом потоке относительно независимы от переменных в других потоках.. Экземпляры ThreadLocal обычноprivate static
Тип, используемый для связывания потока и контекста потока.
Роль ThreadLocal заключается в предоставлении локальных переменных в потоке, которые действуют в жизненном цикле потока и упрощают передачу некоторых общих переменных между несколькими функциями или компонентами в одном потоке.
ThreadLocal не является механизмом синхронизации и не решает проблему условий многопоточной гонки для общих объектов.
ThreadLocal используется, чтобы сбалансировать эффективность с распределением ресурсов потока.
ThreadLocal предоставит каждому потоку независимую копию переменной, тем самым изолируя конфликты одновременного доступа к данным несколькими потоками. Поскольку у каждого потока есть собственная копия переменной, нет необходимости синхронизировать переменную.
ThreadLocal
set(T): сохранить объект
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
get(): когда потокпервый звонокКогда вызывается метод ThreadLocal.get, он будет вызыватьсяinitialValue()чтобы получить начальное значение, в противном случае он возвращает последнее значение из текущего потока выполнения, вызывающего set(T), этот метод позволяет избежать проблем с передачей этого объекта в качестве параметра.
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
(1) Получить текущий поток
(2) Получить карту в соответствии с текущим потоком
(3) Если полученная карта не пуста, используйте ссылку ThreadLocal в качестве ключа на карте, чтобы получить соответствующее значение e на карте, в противном случае перейдите к (5)
(4) Если e не равно нулю, вернуть e.value, иначе перейти к (5)
(5) Если карта пуста или e пуста, значение начального значения получается с помощью функции initialValue, а затем создается новая карта со ссылкой и значением ThreadLocal как firstKey и firstValue.
remove(): удалить значение, связанное с ThreadLocal текущего потока
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
initialValue(): используется для установки начального значения ThreadLocal, по умолчанию возвращает null, эта функция имеет защищенный тип, обычно функция будет перегружена в виде анонимного внутреннего класса для указания начального значения.
protected T initialValue() {
return null;
}
getMap(Thread): получить ThreadLocalMap потока
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
createMap(Thread,T): Инициализировать threadLocals потока, а затем установить ключ-значение
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
createInheritedMap (ThreadLocalMap):
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
следующийHashCode():
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
установитьначальное значение():
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
JDK1.3: считается, что ThreadLocal
JDK1.8: каждый поток поддерживает таблицу сопоставления ThreadLocalMap, ключом этой таблицы сопоставления является сам экземпляр ThreadLocal, а значением является объект, который действительно необходимо сохранить.
Преимущество JDK1.8
- Количество Entry per Map стало меньше: раньше это было количество Thread, а теперь это количество ThreadLocals, что может повысить производительность.
- Когда поток уничтожается, соответствующий ThreadLocalMap также уничтожается, что может уменьшить использование памяти.
Каждый поток имеет свой собственный объект класса ThreadLocalMap, который может хранить в себе собственный объект потока, и каждый поток может правильно обращаться к своему собственному объекту.
java.lang.Thread:
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
ThreadLocal не можетНаследовать от родительского потокаСодержимое ThreadLocal, класс InheritableThreadLocal наследуется от класса ThreadLocal, а значение переменной InheritableThreadLocal будетАвтоматически передается всем дочерним потокам, передавая данные между родительским и дочерним потоками. При создании темы, если все
InheritableThreadLocal значения объекта, то эти значения также будут автоматически переданы дочернему потоку.Если дочерний поток вызывает
get() из InheritableThreadLocal
, то он увидит тот же объект, что и его родительский поток. Чтобы защитить потокобезопасность, вы должны использовать только неизменяемые объекты (после создания,
объекты, состояние которых никогда не будет изменено) используют InheritableThreadLocal, поскольку объекты совместно используются несколькими потоками.
InheritableThreadLocal подходит для передачи данных из родительского потока в дочерний,
например, идентификатор пользователя или идентификатор транзакции, но не объекты с отслеживанием состояния, такие как JDBC
Связь .
private static ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>();
private static InheritableThreadLocal<Integer> inheritableThreadLocal =
new InheritableThreadLocal<>();
@Test
public void inheritableThreadLocal() {
// 父线程
integerThreadLocal.set(1);
inheritableThreadLocal.set(1);
//结果:pool-1-thread-1:null/1
threadFactory.newThread(() -> System.out.println(Thread.currentThread().getName() + ":"
+ integerThreadLocal.get() + "/"
+ inheritableThreadLocal.get())).start();
}
Ссылка
- Сильная ссылка (FinalReference, Finalizer)
Для таких ссылок, как «Object obj = new Object()», пока существует сильная ссылка, сборщик мусора никогда не вернет объект, на который указывает ссылка. Сильная ссылка — это эталонная реализация Java по умолчанию, она будет жить в JVM как можно дольше, и когда на нее не укажет объект, она не будет переработана после выполнения GC. Если у объекта есть сильная ссылка, это как предмет домашнего обихода, и сборщик мусора никогда не переработает его. Когда места в памяти недостаточно, виртуальная машина Java скорее выдаст ошибку OutOfMemoryError, вызывающую ненормальное завершение программы, чем решит проблему нехватки памяти, произвольно освобождая объекты с сильными ссылками. Система JVM используетFinalizer управлять каждымСильный ссылочный объекти добавьте его, когда он будет помечен для очисткиReferenceQueue, и вызвать объектfinalize()метод
Object obj = new Object();
Object strongReference = obj;
Assertions.assertSame(obj, strongReference);
obj = null;
System.gc();
Assertions.assertNull(obj);
Assertions.assertNotNull(strongReference);
Finalizer является подклассом FinalReference. Этот класс финализирован и не может быть унаследован. Фактически JVM работает с Finalizer. Когда класс удовлетворяет условиям для создания экземпляра FinalReference, JVM вызывает Finalizer.register() для его регистрации.
- Мягкий справочник (java.lang.ref.SoftReference)
Object obj = new Object();
SoftReference<Object> softReference = new SoftReference<>(obj);
Assertions.assertNotNull(softReference.get());
obj = null;
System.gc();
Assertions.assertNull(obj);
// SoftReference 只有在 jvm OutOfMemory 之前才会被回收, 所以它非常适合缓存应用
Assertions.assertNotNull(softReference.get());
- Слабая ссылка (java.lang.ref.WeakReference)
Используется для описания второстепенных объектов, но его сила слабее, чем у мягких ссылок.Объекты, связанные со слабыми ссылками, могут существовать только до следующей сборки мусора. Когда работает сборщик мусора, объекты, связанные только со слабыми ссылками, утилизируются независимо от того, достаточно ли текущей памяти.
Object obj = new Object();
WeakReference<Object> weakReference = new WeakReference<>(obj);
Assertions.assertSame(obj, weakReference.get());
obj = null;
System.gc();
Assertions.assertNull(weakReference.get());
Map<Object, Object> weakHashMap = new WeakHashMap<>();
Object key = new Object();
Object value = new Object();
weakHashMap.put(key, value);
Assertions.assertTrue(weakHashMap.containsValue(value));
key = null;
System.gc();
Thread.sleep(1000);
// 一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 entry
Assertions.assertFalse(weakHashMap.containsValue(value));
- Фантомная ссылка (java.lang.ref.PhantomReference)