Глибоке занурення у Java ThreadLocal
Джерело: Devgenios Сьогодні ви дізнаєтеся про ThreadLocal - один з поширених класів, що використовуються при розробці Java-додатків.Що таке ThreadLocal?
Клас ThreadLocal зберігає локальні змінні потоків. Ці змінні ізольовані між різними потоками і можуть бути доступними лише своєму власному потоку. Варіанти застосування ThreadLocal :- Ізоляція даних між потоками.
- Управління сесіями для підключення до бази даних.
- Зберігання трансакційної інформації потоку.
Як використовувати 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 у кожному потоці та виводимо на друк. При виводі отримуємо:
Що під капотом?
Якщо уважно подивитися, то можна зрозуміти, що в ThreadLocal з цього прикладу коду є два важливі методи.-
public T get() {}
-
public void set (T value) {}
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 , а потім збережене значення. Ось схема, яка допоможе розібратися:
-
Кожен потік підтримує посилання ThreadLocalMap .
-
ThreadLocalMap є внутрішнім статичним класом ThreadLocal та використовує клас Entry для зберігання.
-
Ключ ThreadLocalMap є екземпляром ThreadLocal і може мати декілька ThreadLocal .
-
Сам ThreadLocal не зберігає значення, але це ключ для потоку, який допоможе отримати значення ThreadLocalMap .
Клас Scanner в Java
Джерело: Medium Ця публікація допоможе вам ознайомитися з класом Scanner у Java. Клас 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);
Власне кажучи, ми повторабо два попередні рядки, запитабо значення у користувача і зберегли його. Тепер у останньому рядку ми використовували два значення, які взяли у користувача, і які тепер використовуються разом.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ