显示 ThreadLocal 使用要求的示例
An example that shows the requirement of ThreadLocal usage
据我所知,在 Java 中,ThreadLocal class 使我们能够创建虚拟线程范围。所以一个线程不能访问另一个的变量或其他东西。
能否请您提供一些编码人员需要使用 ThreadLocal 的代码,使用 ThreadLocal 后一切正常。
谢谢。
一个比较常见的例子是使用 DateFormat
(或 Random
,但现在它有自己的 ThreadLocalRandom
class)。
class不是线程安全的,但每次需要时创建新对象都需要时间。
当您创建 ThreadLocal<DateFormat>
时,您可以确定每个线程都有自己的 DateFormat
它们可以安全使用,并且不会有不必要的性能影响。
您可以将任何非线程安全单元包装到 ThreadLocal 中以使其成为线程安全的。例如,Java 的 SimpleDateFormat API 不是线程安全的,但您可以使用以下代码片段
使其成为线程安全的
private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat("yyyyMMdd HHmm");
}
};
private DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
public String formatFirstDate() {
return df.format(new Date());
}
public String formatSecondDate() {
return df.format(new Date(0));
}
在上面的代码中,如果两个线程同时调用 formatFirstDate() 和 formatSecondDate(),可能会导致结果混乱,因为 DateFormat 对象不是线程安全的。这个问题可以通过使用Thread Local来解决:-
public static ThreadLocal df = new ThreadLocal() {
protected DateFormat initialValue() {
return new SimpleDateFormat("dd/MM/yyyy");
}
};
public String formatFirstDate() {
return df.get().format(new Date());
}
public String formatSecondDate() {
return df.get().format(new Date(0));
}
在幕后,创建 ThreadLocal 对象时,它实际上在内部创建了一个 HashMap,如下所示:
HashMap<ThreadID,Value> map;
所以当一个特定的线程向线程本地对象添加一个值时,它会将当前线程的 ThreadId 作为 "key" 插入,并将值作为 "value" 插入到 HashMap 中。
map.put(thread.currentthread().getid() , Value );
所以当我们再次从 Thread Local 对象中获取值时,它将执行以下操作:
map.get(thread.currentthread().getid());
所以即使我们只创建了一个 ThreadLOcal 对象的实例,但我们将能够使它们对每个 Thread 都是本地的。
请检查下面的代码。
我们通过将 运行nable 对象传递给它来创建 3 个线程,我们还在构造函数本身中设置每个线程的名称。
在thr 运行()方法中我们将ThreadName设置到Thread Local对象中,线程进入sleep(),同时其他线程可以进入运行()方法并再次将其 thraedName 设置为同一线程本地实例。
线程唤醒后,我们将从线程本地对象取回数据并打印...
public class ThreadLocalDemo {
public static void main(String[] args) {
testLocal oneInstance = new testLocal();
Thread A = new Thread(oneInstance);
Thread B = new Thread(oneInstance);
Thread C = new Thread(oneInstance);
A.start();
try
{
Thread.sleep(400);
}
catch(InterruptedException e){}
B.start();
try
{
Thread.sleep(400);
}
catch(InterruptedException e){}
C.start();
try
{
Thread.sleep(400);
}
catch(InterruptedException e){}
}
}
class testLocal implements Runnable
{
private static final ThreadLocal local = new ThreadLocal<String>(){
@Override
protected String initialValue() {
System.out.println(" local thread initialValue() called ");
return "intial Value";
}
};
@Override
public void run() {
local.set(Thread.currentThread().getName());
try
{
Thread.sleep(2000);
}
catch(InterruptedException e){}
System.out.print(Thread.currentThread().getName() + " run() " );
System.out.print(" called.... ");
System.out.println(local.get());
}
}
使用 ThreadLocal
是一种使每个线程都可以使用信息的便捷方法,通常用于每个请求一个线程模型的 Web 应用程序中。需要为每个请求单独跟踪的请求上下文数据可以使用 ThreadLocal
.
提供。
ThreadLocal
通常用于 Spring Framework,根据所使用的组件,您可以使用 ThreadLocal
表示的各种类型的上下文。
有关现实世界的示例,请参阅:
每个示例的意图和实现都是相似的。例如您想要存储当前请求的 Locale
信息,然后在其他地方可用。 set 方法(例如 setLocaleContext
将信息存储在 ThreadLocal 上)和 get 方法(例如 getLocaleContext
将 return 来自当前请求的 Locale
信息)
最后,我想补充一点,滥用 ThreadLocal
不是一个好主意,不应将其视为良好设计的替代品。因此,如果可以通过传递具有适当字段值的对象来共享相关的上下文信息,那么这应该优于 ThreadLocal
s,后者使信息可以普遍访问。 This article 可能更有用。
仅供参考Yathish Manjunath的回答。
so when a particular Thread add a value to Thread Local object it will insert the current thread's ThreadId as "key" and the Value as "value" in the HashMap.
在Java8中,线程有一个threadLocals
字段,它是ThreadLocal.ThreadLocalMap
的一个实例,作为每个线程局部的键变量,它是线程局部实例本身。
摘自 ThreadLocal.java
的片段。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value); <------ this as the key
else
createMap(t, value);
}
ThreadLocal变量和Thread变量共同唯一确定缓存变量:value。
据我所知,在 Java 中,ThreadLocal class 使我们能够创建虚拟线程范围。所以一个线程不能访问另一个的变量或其他东西。
能否请您提供一些编码人员需要使用 ThreadLocal 的代码,使用 ThreadLocal 后一切正常。
谢谢。
一个比较常见的例子是使用 DateFormat
(或 Random
,但现在它有自己的 ThreadLocalRandom
class)。
class不是线程安全的,但每次需要时创建新对象都需要时间。
当您创建 ThreadLocal<DateFormat>
时,您可以确定每个线程都有自己的 DateFormat
它们可以安全使用,并且不会有不必要的性能影响。
您可以将任何非线程安全单元包装到 ThreadLocal 中以使其成为线程安全的。例如,Java 的 SimpleDateFormat API 不是线程安全的,但您可以使用以下代码片段
使其成为线程安全的private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat("yyyyMMdd HHmm");
}
};
private DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
public String formatFirstDate() {
return df.format(new Date());
}
public String formatSecondDate() {
return df.format(new Date(0));
}
在上面的代码中,如果两个线程同时调用 formatFirstDate() 和 formatSecondDate(),可能会导致结果混乱,因为 DateFormat 对象不是线程安全的。这个问题可以通过使用Thread Local来解决:-
public static ThreadLocal df = new ThreadLocal() {
protected DateFormat initialValue() {
return new SimpleDateFormat("dd/MM/yyyy");
}
};
public String formatFirstDate() {
return df.get().format(new Date());
}
public String formatSecondDate() {
return df.get().format(new Date(0));
}
在幕后,创建 ThreadLocal 对象时,它实际上在内部创建了一个 HashMap,如下所示:
HashMap<ThreadID,Value> map;
所以当一个特定的线程向线程本地对象添加一个值时,它会将当前线程的 ThreadId 作为 "key" 插入,并将值作为 "value" 插入到 HashMap 中。
map.put(thread.currentthread().getid() , Value );
所以当我们再次从 Thread Local 对象中获取值时,它将执行以下操作:
map.get(thread.currentthread().getid());
所以即使我们只创建了一个 ThreadLOcal 对象的实例,但我们将能够使它们对每个 Thread 都是本地的。
请检查下面的代码。
我们通过将 运行nable 对象传递给它来创建 3 个线程,我们还在构造函数本身中设置每个线程的名称。
在thr 运行()方法中我们将ThreadName设置到Thread Local对象中,线程进入sleep(),同时其他线程可以进入运行()方法并再次将其 thraedName 设置为同一线程本地实例。
线程唤醒后,我们将从线程本地对象取回数据并打印...
public class ThreadLocalDemo {
public static void main(String[] args) {
testLocal oneInstance = new testLocal();
Thread A = new Thread(oneInstance);
Thread B = new Thread(oneInstance);
Thread C = new Thread(oneInstance);
A.start();
try
{
Thread.sleep(400);
}
catch(InterruptedException e){}
B.start();
try
{
Thread.sleep(400);
}
catch(InterruptedException e){}
C.start();
try
{
Thread.sleep(400);
}
catch(InterruptedException e){}
}
}
class testLocal implements Runnable
{
private static final ThreadLocal local = new ThreadLocal<String>(){
@Override
protected String initialValue() {
System.out.println(" local thread initialValue() called ");
return "intial Value";
}
};
@Override
public void run() {
local.set(Thread.currentThread().getName());
try
{
Thread.sleep(2000);
}
catch(InterruptedException e){}
System.out.print(Thread.currentThread().getName() + " run() " );
System.out.print(" called.... ");
System.out.println(local.get());
}
}
使用 ThreadLocal
是一种使每个线程都可以使用信息的便捷方法,通常用于每个请求一个线程模型的 Web 应用程序中。需要为每个请求单独跟踪的请求上下文数据可以使用 ThreadLocal
.
ThreadLocal
通常用于 Spring Framework,根据所使用的组件,您可以使用 ThreadLocal
表示的各种类型的上下文。
有关现实世界的示例,请参阅:
每个示例的意图和实现都是相似的。例如您想要存储当前请求的 Locale
信息,然后在其他地方可用。 set 方法(例如 setLocaleContext
将信息存储在 ThreadLocal 上)和 get 方法(例如 getLocaleContext
将 return 来自当前请求的 Locale
信息)
最后,我想补充一点,滥用 ThreadLocal
不是一个好主意,不应将其视为良好设计的替代品。因此,如果可以通过传递具有适当字段值的对象来共享相关的上下文信息,那么这应该优于 ThreadLocal
s,后者使信息可以普遍访问。 This article 可能更有用。
仅供参考Yathish Manjunath的回答。
so when a particular Thread add a value to Thread Local object it will insert the current thread's ThreadId as "key" and the Value as "value" in the HashMap.
在Java8中,线程有一个threadLocals
字段,它是ThreadLocal.ThreadLocalMap
的一个实例,作为每个线程局部的键变量,它是线程局部实例本身。
摘自 ThreadLocal.java
的片段。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value); <------ this as the key
else
createMap(t, value);
}
ThreadLocal变量和Thread变量共同唯一确定缓存变量:value。