Почему ThreadLocal не создается в новом потоке?

#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 Спасибо, к ВАШЕМУ сведению