Вопрос

Когда мне следует использовать ThreadLocal переменная?

Как он используется?

Это было полезно?

Решение

Одно из возможных (и распространенных) применений - это когда у вас есть объект, который не является потокобезопасным, но вы хотите избежать синхронизировать доступ к этому объекту (я смотрю на вас, SimpleDateFormat ). Вместо этого предоставьте каждому потоку свой экземпляр объекта.

Например:

public class Foo
{
    // SimpleDateFormat is not thread-safe, so give one to each thread
    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue()
        {
            return new SimpleDateFormat("yyyyMMdd HHmm");
        }
    };

    public String formatIt(Date date)
    {
        return formatter.get().format(date);
    }
}

Документация .

Другие советы

Поскольку ThreadLocal - это ссылка на данные в данном Thread, вы можете столкнуться с утечками при загрузке классов при использовании get() на серверах приложений с использованием пулов потоков. Вы должны быть очень осторожны при очистке set() своих remove() или java.lang.OutOfMemoryError: PermGen space методов -XX:MaxPermSize <=>.

Если вы не выполните очистку после завершения, все ссылки, которые он содержит на классы, загруженные как часть развернутого веб-приложения, останутся в постоянная куча и никогда не будет получить мусор. Повторное развертывание / удаление веб-приложения не приведет к очистке каждой ссылки <=> на класс (ы) вашего веб-приложения, поскольку <=> не принадлежит вашему веб-приложению. Каждое последующее развертывание создаст новый экземпляр класса, который никогда не будет собирать мусор.

Вы получите исключение нехватки памяти из-за <=>, и после некоторого поиска в Google, вероятно, просто увеличится <=> вместо исправления ошибки.

Если вы столкнулись с этими проблемами, вы можете определить, какой поток и класс сохраняют эти ссылки, используя Анализатор памяти Eclipse и / или следуя Руководство Фрэнка Киевита и продолжение .

Обновление: повторно обнаружил запись в блоге Алекса Вассера это помогло мне отследить некоторые <=> проблемы, с которыми я столкнулся.

Многие фреймворки используют ThreadLocals для поддержки некоторого контекста, связанного с текущим потоком. Например, когда текущая транзакция хранится в ThreadLocal, вам не нужно передавать ее в качестве параметра через каждый вызов метода, если кому-то из стека требуется доступ к нему. Веб-приложения могут хранить информацию о текущем запросе и сеансе в ThreadLocal, чтобы приложение имело к ним легкий доступ. С Guice вы можете использовать ThreadLocals при реализации пользовательских областей для внедренных объектов (по умолчанию Guice < a href = "https://github.com/google/guice/wiki/Scopes" rel = "nofollow noreferrer"> области сервлетов , скорее всего, их также используют).

ThreadLocals - это один из видов глобальных переменных (хотя и немного менее злой, потому что они ограничены одним потоком), поэтому вы должны быть осторожны при их использовании, чтобы избежать нежелательных побочных эффектов и утечек памяти. Разработайте свои API так, чтобы значения ThreadLocal всегда автоматически очищались, когда они больше не нужны, и что неправильное использование API будет невозможно (например, this. ThreadLocals можно использовать для того, чтобы сделать код чище, и в некоторых редких случаях они являются единственным способом заставить что-то работать (у моего текущего проекта было два таких случая; они задокументированы здесь в разделе " Статические поля и глобальные переменные ").

В Java, если у вас есть данные, которые могут различаться для каждого потока, вы можете передать эти данные каждому методу, который нуждается (или может понадобиться) в них, или связать данные с потоком. Передача данных везде может быть работоспособной, если все ваши методы уже должны обойти общий & Quot; context & Quot; переменная.

Если это не так, вы можете не загромождать сигнатуры вашего метода дополнительным параметром. В не поточном мире вы можете решить проблему с помощью Java-эквивалента глобальной переменной. В многопоточном слове эквивалентом глобальной переменной является локальная переменная потока.

В книге Параллелизм Java на практике есть очень хороший пример . Где автор ( Джошуа Блох ) объясняет, как ограничение потока является одним из самых простых способов достижения безопасности потока и ThreadLocal - это более формальное средство поддержания ограничения потока. В конце он также объясняет, как люди могут злоупотреблять им, используя его в качестве глобальных переменных.

Я скопировал текст из упомянутой книги, но код 3.10 отсутствует, так как не так важно понимать, где следует использовать ThreadLocal.

  

Локальные переменные потока часто используются для предотвращения совместного использования в проектах, основанных на изменчивых синглетонах или глобальных переменных. Например, однопоточное приложение может поддерживать глобальное соединение с базой данных, которое инициализируется при запуске, чтобы избежать необходимости передавать Соединение каждому методу. Поскольку соединения JDBC могут быть не поточно-ориентированными, многопоточное приложение, использующее глобальное соединение без дополнительной координации, также не является поточно-ориентированным. Используя ThreadLocal для хранения соединения JDBC, как в ConnectionHolder в Листинге 3.10, каждый поток будет иметь свое собственное соединение.

     

ThreadLocal широко используется при реализации каркасов приложений. Например, контейнеры J2EE связывают контекст транзакции с исполняющим потоком на время вызова EJB. Это легко реализовать с помощью статического Thread-Local, содержащего контекст транзакции: когда код платформы должен определить, какая транзакция выполняется в данный момент, он выбирает контекст транзакции из этого ThreadLocal. Это удобно тем, что уменьшает необходимость передавать информацию о контексте выполнения в каждый метод, но связывает любой код, который использует этот механизм, с платформой.

     

Легко злоупотреблять ThreadLocal, рассматривая его свойство ограничения потока как лицензию на использование глобальных переменных или как средство создания & # 8220; hidden & # 8221; аргументы метода. Как и глобальные переменные, локальные переменные потока могут отвлекать от повторного использования и вводить скрытые связи между классами, поэтому их следует использовать с осторожностью.

По сути, когда вам нужно, чтобы значение переменной зависело от текущего потока , и вам неудобно прикреплять значение к потоку каким-либо другим способом (например, поток подклассов).

Типичный случай, когда какой-то другой фреймворк создал поток , в котором выполняется ваш код, например, контейнер сервлетов, или где имеет смысл использовать ThreadLocal, потому что тогда ваша переменная " в ее логическом месте " (а не переменная, висящая из подкласса Thread или в какой-либо другой хэш-карте).

На моем веб-сайте у меня есть некоторые дополнительные обсуждения и примеры того, когда использовать ThreadLocal это также может представлять интерес.

Некоторые люди рекомендуют использовать ThreadLocal как способ прикрепления " идентификатора потока " для каждого потока в определенных параллельных алгоритмах, где вам нужен номер потока (см., например, Herlihy & amp; Shavit). В таких случаях убедитесь, что вы действительно получаете выгоду!

Документация очень хорошо говорит об этом : " каждый поток, который обращается к [локальной переменной потока] (через свой метод get или set), имеет свою собственную, независимо инициализированную копию переменной ".

Вы используете один, когда каждый поток должен иметь свою собственную копию чего-либо. По умолчанию данные распределяются между потоками.

Сервер Webapp может хранить пул потоков, и перед ответом клиенту необходимо удалить переменную ThreadLocal, поэтому текущий поток может быть повторно использован при следующем запросе.

<Ол>
  • ThreadLocal в Java был представлен в JDK 1.2, но позже был обобщен в JDK 1.5 для обеспечения безопасности типов в переменной ThreadLocal.

  • ThreadLocal может быть связан с областью действия Thread, весь код, который выполняется Thread, имеет доступ к переменным ThreadLocal, но два потока не могут видеть друг друга переменную ThreadLocal.

  • Каждый поток содержит эксклюзивную копию переменной ThreadLocal, которая получает право на сборку мусора после завершения или прекращения потока, обычно или из-за какого-либо исключения, поскольку эти переменные ThreadLocal не имеют никаких других живых ссылок.

  • Переменные ThreadLocal в Java, как правило, являются частными статическими полями в классах и поддерживают свое состояние внутри Thread.

  • Подробнее: ThreadLocal в Java - пример программы и учебника

    Два варианта использования, где можно использовать переменную threadlocal -
    1. Когда у нас есть требование связать состояние с потоком (например, идентификатор пользователя или идентификатор транзакции). Это обычно происходит с веб-приложением, когда каждый запрос к сервлету имеет уникальный идентификатор транзакции, связанный с ним.

    // This class will provide a thread local variable which
    // will provide a unique ID for each thread
    class ThreadId {
        // Atomic integer containing the next thread ID to be assigned
        private static final AtomicInteger nextId = new AtomicInteger(0);
    
        // Thread local variable containing each thread's ID
        private static final ThreadLocal<Integer> threadId =
            ThreadLocal.<Integer>withInitial(()-> {return nextId.getAndIncrement();});
    
        // Returns the current thread's unique ID, assigning it if necessary
        public static int get() {
            return threadId.get();
        }
    }
    

    Обратите внимание, что здесь метод withInitial реализован с использованием лямбда-выражения.
    2- Другой случай использования - когда мы хотим иметь потокобезопасный экземпляр, и мы не хотим использовать синхронизацию, поскольку затраты на производительность с синхронизацией больше. Одним из таких случаев является использование SimpleDateFormat. Так как SimpleDateFormat не является потокобезопасным, мы должны предоставить механизм, обеспечивающий его безопасность.

    public class ThreadLocalDemo1 implements Runnable {
        // threadlocal variable is created
        private static final ThreadLocal<SimpleDateFormat> dateFormat = new ThreadLocal<SimpleDateFormat>(){
            @Override
            protected SimpleDateFormat initialValue(){
                System.out.println("Initializing SimpleDateFormat for - " + Thread.currentThread().getName() );
                return new SimpleDateFormat("dd/MM/yyyy");
            }
        };
    
        public static void main(String[] args) {
            ThreadLocalDemo1 td = new ThreadLocalDemo1();
            // Two threads are created
            Thread t1 = new Thread(td, "Thread-1");
            Thread t2 = new Thread(td, "Thread-2");
            t1.start();
            t2.start();
        }
    
        @Override
        public void run() {
            System.out.println("Thread run execution started for " + Thread.currentThread().getName());
            System.out.println("Date formatter pattern is  " + dateFormat.get().toPattern());
            System.out.println("Formatted date is " + dateFormat.get().format(new Date()));
        } 
    
    }
    

    Начиная с версии Java 8, существует более декларативный способ инициализации. ThreadLocal:

    ThreadLocal<Cipher> local = ThreadLocal.withInitial(() -> "init value");
    

    До выпуска Java 8 вам нужно было сделать следующее:

    ThreadLocal<String> local = new ThreadLocal<String>(){
        @Override
        protected String initialValue() {
            return "init value";
        }
    };
    

    Более того, если метод создания экземпляра (конструктор, фабричный метод) класса, который используется для ThreadLocal не принимает никаких параметров, вы можете просто использовать ссылки на методы (представленные в Java 8):

    class NotThreadSafe {
        // no parameters
        public NotThreadSafe(){}
    }
    
    ThreadLocal<NotThreadSafe> container = ThreadLocal.withInitial(NotThreadSafe::new);
    

    Примечание:Оценка ленива, так как вы проходите java.util.function.Supplier лямбда, которая оценивается только тогда, когда ThreadLocal#get вызывается, но значение ранее не оценивалось.

    Вы должны быть очень осторожны с паттерном ThreadLocal. Есть несколько основных недостатков, как упомянул Фил, но один из них не был упомянут, чтобы убедиться, что код, который устанавливает контекст ThreadLocal, не является & Quot; reenrant. & Quot;

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

    когда?

    Если объект не является потокобезопасным, вместо синхронизации, которая затрудняет масштабируемость, дайте один объект каждому потоку и сохраните его область потока, то есть ThreadLocal.Одними из наиболее часто используемых, но не потокобезопасных объектов являются соединение с базой данных и JMSConnection.

    Как ?

    Одним из примеров является то, что среда Spring активно использует ThreadLocal для скрытого управления транзакциями, сохраняя эти объекты соединения в переменных ThreadLocal.На высоком уровне, когда транзакция запускается, она получает соединение (и отключает автоматическую фиксацию) и сохраняет его в ThreadLocal.при дальнейших вызовах БД он использует то же соединение для связи с БД.В конце он берет соединение из ThreadLocal, фиксирует (или откатывает) транзакцию и освобождает соединение.

    Я думаю, что log4j также использует ThreadLocal для поддержки MDC.

    ThreadLocal полезно, когда вы хотите иметь какое-то состояние, которое не должно быть общим для разных потоков, но оно должно быть доступно из каждого потока в течение всего времени его существования.

    В качестве примера представьте веб-приложение, в котором каждый запрос обслуживается отдельным потоком. Представьте, что для каждого запроса вам нужно многократно обрабатывать один фрагмент данных, что довольно дорого для вычисления. Однако эти данные могли изменяться для каждого входящего запроса, что означает, что вы не можете использовать простой кэш. Простым и быстрым решением этой проблемы будет наличие переменной <=>, содержащей доступ к этим данным, так что вам придется рассчитывать ее только один раз для каждого запроса. Конечно, эту проблему также можно решить без использования <=>, но я разработал ее для наглядности.

    Тем не менее, имейте в виду, что <=> - это, по сути, форма глобального государства. В результате он имеет много других последствий и должен использоваться только после рассмотрения всех других возможных решений.

    Здесь нет ничего нового, но сегодня я обнаружил, что ThreadLocal очень полезен при использовании Bean Validation в веб-приложении. Сообщения валидации локализованы, но по умолчанию используются Locale.getDefault(). Вы можете настроить Validator с другим MessageInterpolator, но нет способа указать Locale при вызове validate. Таким образом, вы можете создать статический ThreadLocal<Locale> (или, что еще лучше, общий контейнер с другими вещами, которые вам могут понадобиться ServletFilter, а затем попросить request.getLocale() выбрать <=> из этого. Следующий шаг - написать <=> который использует значение сеанса или <=>, чтобы выбрать языковой стандарт и сохранить его в своей <=> ссылке.

    Как упомянул @unknown (google), он используется для определения глобальной переменной, в которой указанное значение может быть уникальным в каждом потоке. Обычно его использование влечет за собой хранение некоторой контекстной информации, которая связана с текущим потоком выполнения.

    Мы используем его в среде Java EE для передачи идентификатора пользователя классам, которые не поддерживают Java EE (не имеют доступа к HttpSession или EJB SessionContext). Таким образом, код, который использует идентификатор для операций на основе безопасности, может получить доступ к идентификатору из любого места, без необходимости явно передавать его при каждом вызове метода.

    Цикл операций запрос / ответ в большинстве вызовов Java EE упрощает этот тип использования, поскольку он дает четко определенные точки входа и выхода для установки и сброса ThreadLocal.

      

    ThreadLocal обеспечит доступ к изменяемому объекту несколькими   потоки в несинхронизированном методе синхронизированы, что означает создание   изменяемый объект должен быть неизменным внутри метода.

    Это   достигается путем предоставления нового экземпляра изменяемого объекта для каждого потока   попробуйте получить к нему доступ. Так что это локальная копия каждого потока. Это какой-то   взломать создание переменной экземпляра в методе для доступа, как   локальная переменная. Как вы знаете метод локальная переменная доступна только   для потока одно отличие есть; метод локальных переменных не будет   доступен потоку, когда выполнение метода закончено, где изменяемый   общий объект с threadlocal будет доступен через несколько   методы, пока мы не очистим его.

    По определению:

      

    Класс ThreadLocal в Java позволяет создавать переменные, которые могут   быть только прочитанным и написанным тем же потоком. Таким образом, даже если два потока   выполняем один и тот же код, и код имеет ссылку на   Переменная ThreadLocal, тогда два потока не могут видеть друг друга   Переменные ThreadLocal.

    Каждый Thread в Java содержит ThreadLocalMap в нем.
    Где

    Key = One ThreadLocal object shared across threads.
    value = Mutable object which has to be used synchronously, this will be instantiated for each thread.
    

    Достижение ThreadLocal:

    Теперь создайте класс-оболочку для ThreadLocal, который будет содержать изменяемый объект, как показано ниже (с initialValue() или без него).
    Теперь getter и setter этой оболочки будут работать с локальным экземпляром вместо изменяемого объекта.

    Если getter () из threadlocal не нашел значения с помощью в threadlocalmap из wrapper.getDateFormatter(); затем он вызовет initialValue (), чтобы получить свою личную копию по отношению к потоку.

    class SimpleDateFormatInstancePerThread {
    
        private static final ThreadLocal<SimpleDateFormat> dateFormatHolder = new ThreadLocal<SimpleDateFormat>() {
    
            @Override
            protected SimpleDateFormat initialValue() {
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd") {
                    UUID id = UUID.randomUUID();
                    @Override
                    public String toString() {
                        return id.toString();
                    };
                };
                System.out.println("Creating SimpleDateFormat instance " + dateFormat +" for Thread : " + Thread.currentThread().getName());
                return dateFormat;
            }
        };
    
        /*
         * Every time there is a call for DateFormat, ThreadLocal will return calling
         * Thread's copy of SimpleDateFormat
         */
        public static DateFormat getDateFormatter() {
            return dateFormatHolder.get();
        }
    
        public static void cleanup() {
            dateFormatHolder.remove();
        }
    }
    

    Теперь threadlocal.get() позвонит currentThread.threadLocalMap и проверит, что <=> содержит этот (threadlocal) экземпляр.
    Если да, вернуть значение (SimpleDateFormat) для соответствующего экземпляра локального потока
    иначе добавьте карту с этим локальным экземпляром thread, initialValue ().

    При этом достигается безопасность потоков в этом изменяемом классе; каждый поток работает со своим изменяемым экземпляром, но с тем же экземпляром ThreadLocal. Означает, что весь поток будет использовать тот же экземпляр ThreadLocal, что и ключ, но другой экземпляр SimpleDateFormat в качестве значения.

    https://github.com/skanagavelu/yt. тек / BLOB / Master / SRC / ThreadLocalTest.java

    Локальные переменные потока часто используются для предотвращения совместного использования в проектах, основанных на изменяемые синглтоны или глобальные переменные.

    Его можно использовать в таких случаях, как создание отдельного соединения JDBC для каждого потока, когда вы не используете пул соединений.

    private static ThreadLocal<Connection> connectionHolder
               = new ThreadLocal<Connection>() {
          public Connection initialValue() {
               return DriverManager.getConnection(DB_URL);
              }
         };
    
    public static Connection getConnection() {
          return connectionHolder.get();
    } 
    

    Когда вы вызываете getConnection, он возвращает соединение, связанное с этим потоком. То же самое можно сделать с другими свойствами, такими как dateformat, контекст транзакции, который вы не хотите разделять между потоками.

    Вы могли бы также использовать локальные переменные для одного и того же, но эти ресурсы обычно занимают время при создании, поэтому вы не хотите создавать их снова и снова, когда выполняете с ними некоторую бизнес-логику. Тем не менее, значения ThreadLocal хранятся в самом объекте потока, и как только поток подвергается сборке мусора, эти значения также исчезают.

    Эта ссылка очень хорошо объясняет использование ThreadLocal.

    Класс ThreadLocal в Java позволяет создавать переменные, которые могут быть прочитаны и записаны только одним потоком. Таким образом, даже если два потока выполняют один и тот же код, и код имеет ссылку на переменную ThreadLocal, эти два потока не могут видеть переменные ThreadLocal друг друга.

    Подробнее ...

    [Для справки] ThreadLocal не может решить проблемы обновления общего объекта. Рекомендуется использовать объект staticThreadLocal, который используется всеми операциями в одном потоке. [Обязательный] метод remove () должен быть реализован переменными ThreadLocal, особенно при использовании пулов потоков, в которых потоки часто используются повторно. В противном случае это может повлиять на последующую бизнес-логику и вызвать непредвиденные проблемы, такие как утечка памяти.

    Кэширование, иногда вам приходится вычислять одно и то же значение много времени, поэтому, сохраняя последний набор входных данных для метода и результат, вы можете ускорить код. Используя Thread Local Storage, вам не нужно думать о блокировке.

    ThreadLocal - это специально предоставленная функциональность JVM для предоставления изолированного пространства хранения только для потоков. подобно значению переменной области видимости, привязанной только к данному экземпляру класса. каждый объект имеет свои единственные значения, и они не могут видеть значения друг друга. как и концепция переменных ThreadLocal, они являются локальными для потока в том смысле, что экземпляры объектов, которые другие потоки, кроме той, которая его создала, не могут ее видеть. см. здесь

    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.stream.IntStream;
    
    
    public class ThreadId {
    private static final AtomicInteger nextId = new AtomicInteger(1000);
    
    // Thread local variable containing each thread's ID
    private static final ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> nextId.getAndIncrement());
    
    
    // Returns the current thread's unique ID, assigning it if necessary
    public static int get() {
        return threadId.get();
    }
    
    public static void main(String[] args) {
    
        new Thread(() -> IntStream.range(1, 3).forEach(i -> {
            System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
        })).start();
    
        new Thread(() -> IntStream.range(1, 3).forEach(i -> {
            System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
        })).start();
    
        new Thread(() -> IntStream.range(1, 3).forEach(i -> {
            System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
        })).start();
    
    }
    }
    

    Threadlocal предоставляет очень простой способ достижения повторного использования объектов с нулевой стоимостью.

    У меня была ситуация, когда несколько потоков создавали образ изменяемого кэша при каждом уведомлении об обновлении.

    Я использовал Threadlocal в каждом потоке, а затем каждому потоку нужно было просто сбросить старое изображение и затем обновлять его из кэша при каждом уведомлении об обновлении.

    Обычные повторно используемые объекты из пулов объектов связаны с затратами на безопасность потоков, в то время как у этого подхода нет.

    Существует 3 сценария использования помощник класса например SimpleDateFormat в многопоточном коде, какой лучше всего использовать ThreadLocal

    Сценарии

    1- Использование лайка поделиться объектом с помощью блокировка или синхронизация механизм, который делает приложение медленный

    2- Использование в качестве локальный объект внутри метода

    В этом сценарии, если у нас есть 4 нитки каждый вызывает метод 1000 время, то у нас есть
    4000 SimpleDateFormat объект создан и ждем, пока GC их сотрет

    3- С использованием ThreadLocal

    если бы у нас было 4 потока и мы дали бы в каждом потоке один экземпляр SimpleDateFormat
    так что у нас есть 4 темы, 4 объекта из SimpleDateFormat.

    Нет необходимости в механизме блокировки, создании и уничтожении объектов.(Хорошая временная сложность и пространственная сложность)

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top