Java ThreadLocal используется для создания локальных переменных потока. Мы знаем, что все потоки объекта делят его переменные, поэтому переменная не является потокобезопасной. Мы можем использовать синхронизацию для обеспечения безопасности потока, но если мы хотим избежать синхронизации, мы можем использовать переменные ThreadLocal
.
Java ThreadLocal
У каждого потока есть своя переменная
ThreadLocal
, и они могут использовать его методы get() и set() для получения значения по умолчанию или изменения значения локально для потока. Экземпляры ThreadLocal обычно являются закрытыми статическими полями в классах, которые хотят связать состояние с потоком.
Пример Java ThreadLocal
Вот небольшой пример использования ThreadLocal в программе на Java, подтверждающий, что у каждого потока есть своя копия переменной ThreadLocal. ThreadLocalExample.java
package com.journaldev.threads;
import java.text.SimpleDateFormat;
import java.util.Random;
public class ThreadLocalExample implements Runnable{
// SimpleDateFormat не является потокобезопасным, поэтому предоставляем каждому потоку свой экземпляр
private static final ThreadLocal formatter = new ThreadLocal(){
@Override
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat("yyyyMMdd HHmm");
}
};
public static void main(String[] args) throws InterruptedException {
ThreadLocalExample obj = new ThreadLocalExample();
for(int i=0 ; i<10; i++){
Thread t = new Thread(obj, ""+i);
Thread.sleep(new Random().nextInt(1000));
t.start();
}
}
@Override
public void run() {
System.out.println("Thread Name= "+Thread.currentThread().getName()+" default Formatter = "+formatter.get().toPattern());
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
//здесь шаблон форматирования изменен потоком, но это не отразится на других потоках
formatter.set(new SimpleDateFormat());
System.out.println("Thread Name= "+Thread.currentThread().getName()+" formatter = "+formatter.get().toPattern());
}
}
Вывод приведенной выше программы-примера Java ThreadLocal:
Thread Name= 0 default Formatter = yyyyMMdd HHmm
Thread Name= 1 default Formatter = yyyyMMdd HHmm
Thread Name= 0 formatter = M/d/yy h:mm a
Thread Name= 2 default Formatter = yyyyMMdd HHmm
Thread Name= 1 formatter = M/d/yy h:mm a
Thread Name= 3 default Formatter = yyyyMMdd HHmm
Thread Name= 4 default Formatter = yyyyMMdd HHmm
Thread Name= 4 formatter = M/d/yy h:mm a
Thread Name= 5 default Formatter = yyyyMMdd HHmm
Thread Name= 2 formatter = M/d/yy h:mm a
Thread Name= 3 formatter = M/d/yy h:mm a
Thread Name= 6 default Formatter = yyyyMMdd HHmm
Thread Name= 5 formatter = M/d/yy h:mm a
Thread Name= 6 formatter = M/d/yy h:mm a
Thread Name= 7 default Formatter = yyyyMMdd HHmm
Thread Name= 8 default Formatter = yyyyMMdd HHmm
Thread Name= 8 formatter = M/d/yy h:mm a
Thread Name= 7 formatter = M/d/yy h:mm a
Thread Name= 9 default Formatter = yyyyMMdd HHmm
Thread Name= 9 formatter = M/d/yy h:mm a
Как вы можете видеть из вывода, что Thread-0 изменил значение форматтера, но по-прежнему значение форматтера по умолчанию для Thread-2 остается таким же, как и исходное значение. Вы можете увидеть тот же шаблон и для других потоков. **Обновление**: Класс ThreadLocal расширен в Java 8 с новым методом `withInitial()`, который принимает функциональный интерфейс Supplier в качестве аргумента. Поэтому мы можем использовать лямбда-выражения для легкого создания экземпляра ThreadLocal. Например, переменная ThreadLocal formatter, указанная выше, может быть определена в одной строке следующим образом:
private static final ThreadLocal<SimpleDateFormat> formatter =
ThreadLocal.<SimpleDateFormat>withInitial
(() -> {return new SimpleDateFormat("yyyyMMdd HHmm");});
Если вы новичок в особенностях Java 8, пожалуйста, ознакомьтесь с **Java 8 Features** и **Java 8 Functional Interfaces**. Это все, что касается ThreadLocal в программировании на Java. Ссылка: **API Doc**
Source:
https://www.digitalocean.com/community/tutorials/java-threadlocal-example