JavaRush /Java блог /Random /Кофе-брейк #160. Глубокое погружение в Java ThreadLocal. ...

Кофе-брейк #160. Глубокое погружение в Java ThreadLocal. Класс Scanner в Java

Статья из группы Random

Глубокое погружение в Java ThreadLocal

Источник: Devgenios Сегодня вы узнаете о ThreadLocal — одном из распространенных классов, используемых при разработке Java-приложений. Кофе-брейк #160. Глубокое погружение в Java ThreadLocal. Класс Scanner в Java - 1

Что такое ThreadLocal?

Класс ThreadLocal хранит локальные переменные для потоков. Эти переменные изолированы между разными потоками и могут быть доступны только своему собственному потоку. Варианты применения ThreadLocal:
  1. Изоляция данных между потоками.
  2. Управление сессиями для подключения к базе данных.
  3. Хранение транзакционной информации потока.

Как использовать ThreadLocal?

Давайте рассмотрим простой пример.

public static void main(String[] args) {
    //Создаем ThreadLocal
    ThreadLocal<String> local = new ThreadLocal<>();
    //Создаем новый класс Random
    Random random = new Random();
    //Создаем 5 потоков
    IntStream.range(0, 5).forEach(a-> new Thread(()-> {
        //Присваиваем значение каждому потоку
        local.set(a+"  "+random.nextInt(100));
        System.out.println("Thread number and its local value  "+ local.get());
    }).start());
}
В приведенном выше коде мы создаем класс ThreadLocal, создаем 5 потоков, присваиваем значение ThreadLocal в каждом потоке и выводим на печать. При выводе получаем: Кофе-брейк #160. Глубокое погружение в Java ThreadLocal. Класс Scanner в Java - 2

Что под капотом?

Если посмотреть внимательно, то можно понять, что в ThreadLocal из этого примера кода есть два важных метода.
  • public T get() {}

  • public void set (T value) {}

Давайте посмотрим на метод setter в исходном коде ThreadLocal:

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
}
Метод setter сначала получает текущий поток и вызывает метод getMap() для получения класса ThreadLocalMap. Если map существует, возьмите текущий поток t в качестве ключа, входной параметр в качестве значения и установите пару {key:value} в map. Если нет, то создайте map. Теперь у вас может появиться вопрос — что такое ThreadLocalMap?

static class ThreadLocalMap {
   /**
    * The entries in this hash map extend WeakReference, using
    * its main ref field as the key (which is always a
    * ThreadLocal object).  Note that null keys (i.e. entry.get()
    * == null) mean that the key is no longer referenced, so the
    * entry can be expunged from table.  Such entries are referred to
    * as "stale entries" in the code that follows.
    */
    static class Entry extends WeakReference<ThreadLocal<?>> {
       /** The value associated with this ThreadLocal. */
       Object value;
       Entry(ThreadLocal<?> k, Object v) {
           super(k);
           value = v;
       }
    }
}
ThreadLocalMap — это внутренний статический класс в ThreadLocal, определяющий класс Entry для хранения данных. Entry использует экземпляр ThreadLocal в качестве ключа и устанавливает значение, которое мы передаем. Если на данном этапе это звучит слишком запутанно, просто запомните, что именно класс Entry в ThreadLocalMap выполняет фактическое хранение значений. Чтобы получить данные из ThreadLocal, мы используем метод getter:

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();
}
В методе getter мы будем использовать currentThread в качестве ключа для получения ThreadLocalMap. Затем map получит getEntry() на основе экземпляра ThreadLocal и вернет экземпляр Entry, а затем сохраненное значение. Вот схема, которая поможет разобраться: Кофе-брейк #160. Глубокое погружение в Java ThreadLocal. Класс Scanner в Java - 3Кофе-брейк #160. Глубокое погружение в Java ThreadLocal. Класс Scanner в Java - 4
  1. Каждый поток поддерживает ссылку на ThreadLocalMap.

  2. ThreadLocalMap является внутренним статическим классом ThreadLocal и использует класс Entry для хранения.

  3. Ключ ThreadLocalMap является экземпляром ThreadLocal и может иметь несколько ThreadLocal.

  4. Сам ThreadLocal не хранит значение, но это ключ для потока, который поможет получить значение из ThreadLocalMap.

Обратите внимание, что лучше удалить ThreadLocal, чтобы избежать OOM (Out-of-Memory Error, ошибка нехватки памяти) из-за “слабой” ссылки в классе Entry.

Класс Scanner в Java

Источник: Medium Эта публикация поможет вам ознакомиться с классом Scanner в Java. Кофе-брейк #160. Глубокое погружение в Java ThreadLocal. Класс Scanner в Java - 5Класс Scanner в Java — это класс, который мы используем, когда хотим получить значение от пользователя. Разобраться в нем легче всего на примерах, поэтому давайте разберем его более наглядно. Создание класса Scanner — это один из трех шагов, которые мы предпринимаем, чтобы получить значение от пользователя. Первый шаг — это создать объект из класса сканера.

Scanner scan=new Scanner(System.in);
Теперь у нас есть объект сканера. Этот объект будет иметь свойства Scanner из класса Scanner. После первого шага пользователь уже может ввести желаемое значение, но если мы не направим пользователя и не отобразим значение в консоли или в приложении, это будет не очень хорошо с точки зрения удобства. Поэтому лучше проинформировать и направить пользователя:

System.out.println("Please enter your name");    
String name=scan.next();   
System.out.println("Your Name:"+name);
В первой строке мы спрашиваем пользователя, что мы от него хотим. На самом деле это не имеет ничего общего со сканером, но всегда полезно давать подсказки своим юзерам. Во второй строке мы присвоим значение, которое пользователь введёт в данные, и сохраним его, чтобы мы могли использовать его позже. В последней строке мы видим, что можем использовать полученное от пользователя значение по своему усмотрению. Давайте добавим в наш код еще несколько деталей:

System.out.println("Please enter your last name");
  
String lastName=scan.next();
  
System.out.println("Your Name " + name + " " + "Your Last Name" + lastName);
Собственно говоря, мы повторили предыдущие две строки, запросили значение у пользователя и сохранили его. Теперь в последней строке мы использовали два значения, которые взяли у пользователя, и которые теперь используются вместе.
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
ephyl Уровень 26
24 ноября 2023
Про ThreadLocal ок, но при чем тут вторая статья про сканнер ? Нарушение SOLID))