#java #multithreading #thread-safety
#java #многопоточность #потокобезопасность
Вопрос:
Сборщик классов был создан для генерации карты со значениями (например). Необходимо, чтобы коллектор содержал ресурс, к которому имеет доступ только текущий поток.
Сборщик классов представлен ниже:
public class Collector
{
ThreadLocal<Map<String, String>> storage;
public Map<String, String> collection(int id) {
storage = new ThreadLocal<>();
storage.set(new HashMap<>());
for (int i = id * 100; i <= id * 1000; i =id * 100)
{
storage.get().put(String.valueOf(i), "test");
}
return storage.get();
}
}
Я пытаюсь выполнить метод collection(int id)
одновременно в разных потоках. Мое предложение основано на официальной документации. Но иногда возникает исключение NullPointerException, мои наблюдения указывают на повторное создание ThreadLocal другим потоком, поэтому возникает исключение NullPointerException storage.get().put(String.valueOf(i), "test");
, потому что в другом потоке строка storage = new ThreadLocal<>();
была повторно инициализирована.
Ниже представлен код, в котором выполняются два потока:
Collector collector = new Collector();
new Thread(new Runnable()
{
@Override
public void run()
{
System.out.println(collector.collection(1));
}
}).start();
new Thread(new Runnable()
{
@Override
public void run()
{
System.out.println(collector.collection(2));
}
}).start();
Как я могу использовать ThreadLocal, чтобы иметь локальный ресурс в каждом потоке, который является независимым от других потоков?
Комментарии:
1. Ваш метод не использует никакого общего состояния, кроме ThreadLocal. Он просто создает новую локальную хэш-карту и возвращает ее. Это было бы потокобезопасно, если бы вы не напортачили с ThreadLocal. Для этого метода вам не нужен никакой ThreadLocal, какая-либо синхронизация или что-либо еще.
2. Это просто пример. Это необходимо, потому что мне нужно создать ресурс, который необходим для использования в другом методе в сборщике.
Ответ №1:
Проблема в том, что вы заново создаете новый ThreadLocal
экземпляр каждый раз, когда
collection(int)
вызывается метод с
this.storage = new ThreadLocal<>();
ThreadLocal<Map<String, String>>
Может быть полем класса static
private final static ThreadLocal<Map<String, String>> STORAGE = new ThreadLocal<>();
Чтобы установить и получить Thread
связанное значение, просто используйте
// You're better off passing and using an already constructed instance
STORAGE.set(new HashMap<>());
STORAGE.get();
Комментарии:
1. @j6wj1997 С использованием метода get() ThreadLocal. Он получит то, что текущий поток ранее сохранил, используя set(). В этом весь смысл ThreadLocal.
2. @JBNizet добавлено к ответу.
3. @JB Nizet Спасибо, к ВАШЕМУ сведению