вопрос
Для веб-проектов каждый раз, когда пользователь входит в систему, информация о сеансе будет сохраняться в redis, а затем возвращаться в клиентский токен, который используется как идентификатор сеанса пользователя, после чего все запросы будут проходить через перехватчик, который будет использовать токен для хранения информации о сеансе.Возьмите его из Redis и поместите в ThreadLocal. При большом количестве параллелизма в информации о сеансе будет ошибка.
Процесс анализа ошибки: Пользователь 1 входит в систему и получает доступ к системе.В это время используется поток A, и информация о сеансе пользователя 1 сохраняется в TheadLocal потока A. Затем пользователь 2, который незарегистрированный также получает доступ к системе, и сервер снова назначает поток.A обрабатывает запрос, и пользователь 2 получает информацию о сеансе пользователя 1 в это время.
ThreadLocal
TheadLocal — это полезный встроенный в поток инструмент с общими переменными, предоставляемый jdk. Во время веб-разработки серверная сторона может использовать TheadLocal для хранения запроса и ответа на запрос. Он не передается в стеке вызова метода явно. Очень удобно получать сохраненные нами переменные экземпляра в любом месте стека вызовов методов внутри потока.
class WebContext {
private TheadLocal<HttpServletRequest> request = new TheadLocal<>();
private TheadLocal<HttpServletResponse> response = new TheadLocal<>();
public static void setRequest(HttpServletRequest req) {
request.set(req);
}
public static HttpServletRequest getRequest() {
request.get();
}
public static void setResponse(HttpServletResponse resp) {
response.set(resp);
}
public static HttpServletResponse getResponse() {
response.get();
}
}
class AbcController {
public Object userInfo() {
HttpServletRequest request = WebContext.getRequest();
// request.getParameter()
// ...
}
}
Взгляните на код TheadLocal:
// TheadLocal两个重要的方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
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();
}
Независимо от того, является ли TheadLocal методом установки или методом получения, он сначалаThread t = Thread.currentThread();
, получить текущий экземпляр потока. Затем получите экземпляр TheadLocalMap в экземпляре потока.ThreadLocalMap map = getMap(t);
Вот почему получение переменных потока в одном и том же потоке приведет к получению одного и того же экземпляра.Поскольку каждый экземпляр потока хранит свои собственные переменные потока, эти переменные потокобезопасны.
public class Thread implements Runnable {
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
}
Вышеупомянутое объявлено в классе ThreadthreadLocals
Атрибуты. Можно видеть, что фактическая переменная потока храненияThreadLocal.ThreadLocalMap
Класс, в котором хранятся пары ключ-значение (Key-Value), аналогичные Map, где Key — экземпляр потока, а Value — переменная потока.
Пул потоков Tomcat
Поскольку создание потоков операционной системой очень ресурсоемко, многие приложения будут использовать пулы потоков.Инициализация системы заключается в создании определенного количества потоков за один раз, и эти потоки будут повторно использоваться и совместно использоваться, то есть потоки не будут использоваться после того, как они будут использованы, будут уничтожены до тех пор, пока приложение не будет остановлено.
В результате разные пользователи запрашивают мультиплексирование одного и того же потока, что приводит к получению одной и той же информации о сеансе разными пользователями, что приводит к неупорядоченной информации о сеансе.
решать
Настроить перехватчик на запрос前端
Установите информацию о сеансе в локальные переменные потока, в后端
Очистите информацию о сеансе переменной локального потока.
// 代码简化了
public class SessionInspector extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(...) throws Exception {
// ThreadLocal.set(Session)
return true;
}
public void afterCompletion(...) {
// ThreadLocal.clear()
}
}
Суммировать
ThreadLocal предоставляет нам удобный способ совместного использования переменных в потоках и обеспечивает безопасность потока данных. Но рассмотрим случай повторного использования потока в многопоточной среде, еслиThreadLocal
Сохраняемая информация — это сеанс и другая информация, связанная с пользователем, которая очищается в конце задачи потока.ThreadLocal
информация внутри.