Цель ThreadLocal? [дубликат]

Глупый вопрос, но Вы работаете в режиме отладки? Я сделал ту же ошибку и понял, что был в режиме выпуска.

33
задан cassiomolin 26 October 2018 в 15:30
поделиться

6 ответов

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

Лучше всего объяснить это на примере. Скажем, у вас есть сервлет, который получает зарегистрированного пользователя и затем выполняет некоторый код.

doGet(HttpServletRequest req, HttpServletResponse resp) {
  User user = getLoggedInUser(req);
  doSomething()
  doSomethingElse()
  renderResponse(resp)
}

Что же произойдет, если методам doSomething () потребуется доступ к объекту пользователя? Вы не можете сделать пользовательский объект экземпляром или статической переменной, потому что тогда каждый поток будет использовать один и тот же пользовательский объект. Вы можете передать пользовательский объект в качестве параметра, но это быстро становится беспорядочным и приводит к утечке пользовательских объектов при каждом вызове метода:

doGet(HttpServletRequest req, HttpServletResponse resp) {
  User user = getLoggedInUser(req);
  doSomething(user)
  doSomethingElse(user)
  renderResponse(resp,user)
}

Более элегантное решение - поместить пользовательский объект в ThreadLocal.

doGet(HttpServletRequest req, HttpServletResponse resp) {
  User user = getLoggedInUser(req);
  StaticClass.getThreadLocal().set(user)
  try {
    doSomething()
    doSomethingElse()
    renderResponse(resp)
  }
  finally {
    StaticClass.getThreadLocal().remove()
  }
}

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

User user = StaticClass.getThreadLocal().get()

Если вы используете этот подход, не забудьте снова удалить объекты в блоке finally. В противном случае пользовательский объект может зависать в средах, которые используют пул потоков (например, сервер приложений Tomcat).

Изменить: код для статического класса

class StaticClass {
  static private ThreadLocal<User> threadLocal = new ThreadLocal<>();

  static ThreadLocal<User> getThreadLocal() {
    return threadLocal;
  }
}
80
ответ дан 27 November 2019 в 17:31
поделиться

Вы должны понимать, что экземпляр класса, расширяющего Thread, не то же самое, что реальный поток Java (который можно представить как "указатель выполнения", который проходит через ваш код и выполняет его).

Экземпляры такого класса представляют поток Java и позволяют манипулировать им (например, прерывать его), но помимо этого они являются обычными объектами, и к их членам можно получить доступ из всех потоков, которые могут получить ссылку на объект (что несложно ).

Конечно, вы можете попытаться сохранить член приватным и убедитесь, что он используется только run () или вызываемыми из него методами (общедоступные методы также можно вызывать из других потоков),но это подвержено ошибкам и на самом деле неосуществимо для более сложной системы, где вы не хотите хранить все данные в подклассе Thread (на самом деле вы не должны создавать подкласс Thread, а вместо этого использовать Runnable).

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

13
ответ дан 27 November 2019 в 17:31
поделиться

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

Позвольте мне объяснить идею связывания результата с конкретным потоком. Суть ThreadLocal примерно такая:

public class MyLocal<T> {
  private final Map<Thread, T> values = new HashMap<Thread, T>();

  public T get() {
    return values.get(Thread.currentThread());
  }

  public void set(T t) {
    values.put(Thread.currentThread(), t);
  }
}

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

8
ответ дан 27 November 2019 в 17:31
поделиться

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

6
ответ дан 27 November 2019 в 17:31
поделиться

В википедии есть запись об этой проблемной области . В нашей среде он обычно используется для того, чтобы запросы оставались локальными. На стороне сервера запрос обрабатывается в основном одним потоком. Чтобы вещи оставались локальными, вы помещаете свои данные, например, данные сеанса, в локальной переменной потока. Эти данные невидимы для других запросов (потоков), и поэтому вам не нужно синхронизировать их с другими запросами.

И не забывайте, что существуют конструкции JAVA API, которые не потокобезопасны, например DateFormat . Статический экземпляр DateFormat просто не работает на стороне сервера.

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

2
ответ дан 27 November 2019 в 17:31
поделиться

Преимущество ThreadLocals в том, что их можно использовать by методы, работающие на простых ванильных потоках ... или любом подклассе Thread.

Напротив, если локальные переменные вашего потока должны быть реализованы как члены настраиваемого подкласса Thread, вы не сможете сделать много вещей. Например, у вашего приложения возникнут проблемы, если ему потребуется запускать методы на уже существующих экземплярах vanilla Thread; т.е. экземпляры, которые были созданы некоторым библиотечным кодом, который автор приложения не писал и не может изменять.

1
ответ дан 27 November 2019 в 17:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: