显示 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 不是一个好主意,不应将其视为良好设计的替代品。因此,如果可以通过传递具有适当字段值的对象来共享相关的上下文信息,那么这应该优于 ThreadLocals,后者使信息可以普遍访问。 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。