предисловие
То, что я хочу изучить сегодня, — это ThreadLocal, с которым я столкнулся, когда изучал основы JavaWeb год назад. В то время первый пост в блоге, который ThreadLocal нашел на baidu. Дал много нужных ссылок на исправление (но оригинала блоггера может и не быть в блоге, и он не был изменен). Я тоже ходил изучать его, но к сожалению не было зафиксированного в то время привычки.До сих пор помню только некоторые меха, которые я выучил в то время.
Поэтому очень важно сделать некоторые технические записи~ В то же время, ThreadLocal также является очень распространенным вопросом на собеседовании, а также необходимым знанием для разработчиков Java~
Конечно, если я допустил ошибку, пожалуйста, простите меня и оставьте сообщение под комментариями, чтобы исправить меня~
1. Что такое ThreadLocal
Отказ от ответственности: в этой статье используется JDK 1.8.
Во-первых, давайте взглянем на документацию JDK:
/**
* 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).
*
* <p>For example, the class below generates unique identifiers local to each
* thread.
* A thread's id is assigned the first time it invokes {@code ThreadId.get()}
* and remains unchanged on subsequent calls.
*/
В сочетании с моим резюме это можно понять так: ThreadLocal предоставляет локальные переменные потока, и каждый поток может передаватьset()
иget()
Для работы в этой локальной перементе без конфликтующих с локальными переменными других потоков,Реализована изоляция данных потоков~.
Вкратце: переменные, заполненные в ThreadLocal, принадлежатТекущийпоток, переменная изолирована от других потоков.
2. Зачем учиться ThreadLocal?
Сверху, threadlocal может заставить нас иметь текущую переменную потока, какая польза от этой роли? ? ?
2.1 Управление подключением
**Наиболее типичный из них — управление подключением к базе данных: **Когда я изучал JDBC, я написал простой пул подключений к базе данных для удобства работы.Причина необходимости пула подключений к базе данных также очень проста.Часто создание и закрытие соединений — очень затратная задача, операция с ресурсами, поэтому вам нужно создать пул соединений с базой данных~
Итак, как управлять подключением пула подключений к базе данных? ? Мы оставляем это ThreadLocal для управления. Зачем оставлять это, чтобы управлять им? ? ThreadLocal может реализоватьВсе операции текущего потока используют одно и то же соединение, что обеспечивает транзакцию!
Код, написанный в то время:
public class DBUtil {
//数据库连接池
private static BasicDataSource source;
//为不同的线程管理连接
private static ThreadLocal<Connection> local;
static {
try {
//加载配置文件
Properties properties = new Properties();
//获取读取流
InputStream stream = DBUtil.class.getClassLoader().getResourceAsStream("连接池/config.properties");
//从配置文件中读取数据
properties.load(stream);
//关闭流
stream.close();
//初始化连接池
source = new BasicDataSource();
//设置驱动
source.setDriverClassName(properties.getProperty("driver"));
//设置url
source.setUrl(properties.getProperty("url"));
//设置用户名
source.setUsername(properties.getProperty("user"));
//设置密码
source.setPassword(properties.getProperty("pwd"));
//设置初始连接数量
source.setInitialSize(Integer.parseInt(properties.getProperty("initsize")));
//设置最大的连接数量
source.setMaxActive(Integer.parseInt(properties.getProperty("maxactive")));
//设置最长的等待时间
source.setMaxWait(Integer.parseInt(properties.getProperty("maxwait")));
//设置最小空闲数
source.setMinIdle(Integer.parseInt(properties.getProperty("minidle")));
//初始化线程本地
local = new ThreadLocal<>();
} catch (IOException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
if(local.get()!=null){
return local.get();
}else{
//获取Connection对象
Connection connection = source.getConnection();
//把Connection放进ThreadLocal里面
local.set(connection);
//返回Connection对象
return connection;
}
}
//关闭数据库连接
public static void closeConnection() {
//从线程中拿到Connection对象
Connection connection = local.get();
try {
if (connection != null) {
//恢复连接为自动提交
connection.setAutoCommit(true);
//这里不是真的把连接关了,只是将该连接归还给连接池
connection.close();
//既然连接已经归还给连接池了,ThreadLocal保存的Connction对象也已经没用了
local.remove();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Точно так же Hibernate использует тот же метод для управления соединениями (используя ThreadLocal, конечно, реализация Hibernate более мощная) ~
2.2 Избегайте передачи некоторых параметров
Избегайте понимания передачи некоторых параметровВы можете обратиться к Cookie и Session:
- Всякий раз, когда я захожу на страницу, браузер помогает нам найти соответствующий файл cookie на жестком диске и отправить его.
- Браузер очень умный, он не будет отправлять куки с других сайтов, а будет брать куки только с текущего сайта.
Браузер эквивалентен нашему ThreadLocal, он отправляет только файлы cookie, которые существуют в нашем текущем браузере (локальные переменные ThreadLocal), а разные браузеры изолируют файлы cookie (файлы cookie Chrome, Opera, IE изолированы [в Chrome]. Вы вошли в систему, у вас есть чтобы снова войти в IE]), то же самое: переменные ThreadLocal между потоками также изолированы....
Избегает ли вышеизложенное передачу параметров? ? На самом деле избегали. Файлы cookie не передаются нами вручную и не нуждаются в записи<input name= cookie/>
передать параметры...
То же самое и в программировании: в повседневной жизни, когда мы идем по делам, мы можем использовать удостоверения личности и различные документы во многих местах, которые нам каждый раз доставать очень хлопотно.
// 咨询时要用身份证,学生证,房产证等等....
public void consult(IdCard idCard,StudentCard studentCard,HourseCard hourseCard){
}
// 办理时还要用身份证,学生证,房产证等等....
public void manage(IdCard idCard,StudentCard studentCard,HourseCard hourseCard) {
}
//......
И если используется ThreadLocal, ThreadLocal эквивалентен организации, а организация ThreadLocal записывает, что у вас так много документов. Когда вам это нужно, вам не нужно платить за это самостоятельно, вы можете просто попросить агентство получить его.
На консультации я сказал агентству: приезжайте, отдайте ему мое удостоверение личности, сертификат на недвижимость и студенческий билет. В момент оформления я сказал агентству: давай, отдай ему мой паспорт, свидетельство о недвижимости и студенческий билет. ...
// 咨询时要用身份证,学生证,房产证等等....
public void consult(){
threadLocal.get();
}
// 办理时还要用身份证,学生证,房产证等等....
public void takePlane() {
threadLocal.get();
}
Не будет ли это удобнее, чем платить за это самому?
Конечно, у ThreadLocal могут быть и другие функции получше, если вы знаете, то можете оставить сообщение в комментариях~~~
В-третьих, принцип реализации ThreadLocal
Если вы хотите лучше понять ThreadLocal, вам нужно посмотреть, как он реализован~~~
Отказ от ответственности: в этой статье используется JDK 1.8.
Во-первых, давайте взглянем на метод set() класса ThreadLocal, поскольку мы обычно используем новый объект, поэтому мы устанавливаем объект внутри.
public void set(T value) {
// 得到当前线程对象
Thread t = Thread.currentThread();
// 这里获取ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 如果map存在,则将当前线程对象t作为key,要存储的对象作为value存到map里面去
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
На нем есть ThreadLocalMap, посмотрим что это?
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//....很长
}
Из вышеизложенного мы можем найти, чтоThreadLocalMap — это внутренний класс ThreadLocal. Используйте класс Entry для хранения
нашЗначения хранятся в этой карте, а ключом является текущий объект ThreadLocal!
Если карта не существует, инициализируйте ее:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
Если карта существует, тоПолучить из темы!
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
Thread поддерживает переменную ThreadLocalMap
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null
Как видно из вышеизложенного,ThreadLocalMap написан с использованием внутренних классов в ThreadLocal, но ссылка на объект находится в Thread!
Итак, мы можем сделать вывод:Поток поддерживает карту, такую как ThreadLocalMap, для каждого потока, а ключ ThreadLocalMap — это сам объект LocalThread, а значение — это объект, который нужно сохранить.
С приведенной выше основой совсем несложно понять метод get():
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();
}
3.1 Резюме принципа ThreadLocal
- Каждый поток поддерживает ссылку на ThreadLocalMap
- ThreadLocalMap — это внутренний класс ThreadLocal, который использует Entry для хранения
- При вызове метода set() класса ThreadLocal он фактически устанавливает значение ThreadLocalMap, ключом является объект ThreadLocal, а значение передается в объекте.
- При вызове метода get() класса ThreadLocal он фактически получает значение из ThreadLocalMap, а ключом является объект ThreadLocal.
- Сам ThreadLocal не хранит значения,простоКак ключ, позволяющий потоку получить значение из ThreadLocalMap.
Из-за этого принципа можно реализовать ThreadLocal «изоляцию данных», чтобы получить локальные значения текущих потоков, не влияя на другие потоки -
В-четвертых, избегайте утечек памяти
Давайте взглянем на справочную диаграмму объектных отношений ThreadLocal:
Источник утечки памяти ThreadLocal:Поскольку жизненный цикл ThreadLocalMap такой же длинный, как и у Thread, если соответствующий ключ не будет удален вручную, это вызовет утечку памяти, а не из-за слабых ссылок..
Чтобы избежать утечек памятиРучное удаление ()!
V. Резюме
В этой области ThreadLocal действительно бесчисленное множество сообщений в блогах, и многие, многие просто ищут ~ Стоя на плечах предшественников и резюмируя этот пост в блоге ~
Последнее, что нужно помнить:Цель дизайна ThreadLocal - иметь свои собственные переменные в текущем потоке, а не решать проблему параллелизма или общих переменных.
Если вам недостаточно весело, и вы не думаете, что достаточно всесторонне, вы можете обратиться к ссылке ниже, Многие блоггеры также разработали некоторые расширенные знания, поэтому я не буду расширять их по одному. ~
Справочная запись в блоге:
- blog.Xiao Hansong.com/2016/08/06/…
- Блог Woo Woo.cn on.com/Zhangjiakou 1993…
- блог woo woo woo.cn на.com/dolphin0520…
- блог woo woo woo.cn на.com/dolphin0520…
- Уууу. ITeye.com/topic/10380…
- Блог Woohoo.cn на.com/download blog/afraid/7…
- blog.CSDN.net/U012834750/…
- blog.CSDN.net/win will2012…
- nuggets.capable/post/684490…
Если в статье есть какие-либо ошибки, пожалуйста, поправьте меня, и мы сможем общаться друг с другом. Учащиеся, привыкшие читать технические статьи в WeChat и желающие получить больше ресурсов по Java, могутОбратите внимание на публичный аккаунт WeChat: Java3y