ThreadLocal так просто

Java задняя часть база данных .NET

предисловие

То, что я хочу изучить сегодня, — это 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

  1. Каждый поток поддерживает ссылку на ThreadLocalMap
  2. ThreadLocalMap — это внутренний класс ThreadLocal, который использует Entry для хранения
  3. При вызове метода set() класса ThreadLocal он фактически устанавливает значение ThreadLocalMap, ключом является объект ThreadLocal, а значение передается в объекте.
  4. При вызове метода get() класса ThreadLocal он фактически получает значение из ThreadLocalMap, а ключом является объект ThreadLocal.
  5. Сам ThreadLocal не хранит значения,простоКак ключ, позволяющий потоку получить значение из ThreadLocalMap.

Из-за этого принципа можно реализовать ThreadLocal «изоляцию данных», чтобы получить локальные значения текущих потоков, не влияя на другие потоки -

В-четвертых, избегайте утечек памяти

Давайте взглянем на справочную диаграмму объектных отношений ThreadLocal:

Источник утечки памяти ThreadLocal:Поскольку жизненный цикл ThreadLocalMap такой же длинный, как и у Thread, если соответствующий ключ не будет удален вручную, это вызовет утечку памяти, а не из-за слабых ссылок..

Чтобы избежать утечек памятиРучное удаление ()!

V. Резюме

В этой области ThreadLocal действительно бесчисленное множество сообщений в блогах, и многие, многие просто ищут ~ Стоя на плечах предшественников и резюмируя этот пост в блоге ~

Последнее, что нужно помнить:Цель дизайна ThreadLocal - иметь свои собственные переменные в текущем потоке, а не решать проблему параллелизма или общих переменных.

Если вам недостаточно весело, и вы не думаете, что достаточно всесторонне, вы можете обратиться к ссылке ниже, Многие блоггеры также разработали некоторые расширенные знания, поэтому я не буду расширять их по одному. ~

Справочная запись в блоге:

Если в статье есть какие-либо ошибки, пожалуйста, поправьте меня, и мы сможем общаться друг с другом. Учащиеся, привыкшие читать технические статьи в WeChat и желающие получить больше ресурсов по Java, могутОбратите внимание на публичный аккаунт WeChat: Java3y