【问题标题】:java.lang.ThreadLocal - How each Thread has its own, independently initialized copy of the variable?java.lang.ThreadLocal - 每个线程如何拥有自己的、独立初始化的变量副本?
【发布时间】:2012-07-06 10:20:18
【问题描述】:

关于 java.lang.ThreadLocal 类的 javadoc 让我感到困惑。他们说每个访问线程局部变量的线程都有自己的,独立初始化的变量副本。 这是一个例子(不是真实的例子),它证明了线程局部变量中保存的变量可以被许多线程共享:

package com.mohamad.test.threadlocal;

import java.util.List;

public class ThreadLocalExample {

    private static final ThreadLocal<List<Integer>> myThreadLocal = new ThreadLocal<List<Integer>>();

    public static List<Integer> get() {
        return (myThreadLocal.get());
    }

    public static void set(List<Integer> value) {
        myThreadLocal.set(value);
    }
}


package com.mohamad.test.threadlocal;

import java.util.ArrayList;
import java.util.List;


public class TestThreadLocal implements Runnable {

    private static List<Integer> MY_TEST_LIST = new ArrayList<Integer>(){
        /** The serialVersionUID */
        private static final long serialVersionUID = -2419885728976816054L;
        {add(1);}
    };


    /* (non-Javadoc)
     * @see java.lang.Runnable#run()
     */
    public void run() {
        ThreadLocalExample.set(MY_TEST_LIST);
        List<Integer> integers = ThreadLocalExample.get();
        integers.remove(0);
        System.out.println(Thread.currentThread().getName() + " finished successfully, The list's size is: "  + ThreadLocalExample.get().size() + "\n");
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        TestThreadLocal thread1 = new TestThreadLocal();
        Thread t1 = new Thread(thread1);
        t1.start();
        TestThreadLocal thread2 = new TestThreadLocal();
        Thread t2 = new Thread(thread2);
        t2.start();
    }
}

如果我们运行这个例子,java.lang.IndexOutOfBoundsException 将被抛出,因为 MY_TEST_LISTthread1thread2 共享。 (正如我们看到的,当thread1和thread2调用ThreadLocalExampleset(MY_TEST_LIST)方法,调用ThreadLocal变量的set方法时,它并没有创建MY_TEST_LIST的独立本地副本)

如果有人已经问过这个问题,请给出答案的链接,因为我在 google 上进行研究时没有发现任何有趣的东西。

问候,

【问题讨论】:

  • A ThreadLocal&lt;T&gt; 可以被视为 Map&lt;Thread,T&gt; 并带有一些额外的方法来控制初始化。

标签: java thread-local


【解决方案1】:

一切都很好。 ThreadLocal 中的变量 hold 是线程本地的。在您的情况下,它是本地的 reference,而不是列表本身。每个线程都有自己的引用副本,但所有这些引用都指向同一个位置。换句话说:每个线程都可以保留对不同 List 的引用,但在您的情况下,它们都指向同一个。

如果您希望您的示例正常工作,每个ThreadLocal 应该指向不同的ArrayList(副本):

myThreadLocal.set(new ArrayList<Integer>(value));

ThreadLocals 都指向同一个对象没有多大意义,因为在这种情况下,您只需要一个全局可用的引用。

【讨论】:

  • 他们为什么不把这些称为List 类似ListRef 我想知道... new 是您需要消除歧义的东西,我的意思是用简单的英语。可以说,它实际上似乎并不符合 Java 保留的命名约定,即使它是历史性的。
【解决方案2】:

您在两个线程局部变量中存储了相同的列表引用。这并不意味着 ThreadLocal 没有每个线程的值。您的测试应该启动一个线程,该线程将某些内容存储到线程本地,然后启动另一个线程来查看线程本地变量是否包含任何内容(它不会)。

这就像您有两个地图,并在两个地图中存储了相同的列表。显然,如果您修改存储在一个映射中的列表,则存储在另一个映射中的列表将被修改,因为它是同一个列表。但是清除一张地图并不能清除另一张地图。

【讨论】:

    【解决方案3】:

    ThreadLocal 并不强制每个线程具有不同的引用,它允许每个线程可能拥有自己的副本,具体取决于您的设置方式。

    就像每个对象都有自己的字段一样,您仍然可以将每个对象中的字段设置为相同的值,也可以将值设置为不同的值。

    可能值得阅读 java.lang.Threads 类的源代码以了解 ThreadLocal 是如何实际实现的。

    【讨论】:

    • 我完全同意你的看法!但我认为它在 javadoc 中没有得到很好的解释。开发人员必须阅读 java.lang.ThreadLocal 的源代码才能知道它是如何工作的。
    • 如果您只需要知道如何使用它,而不知道它是如何工作的,您会在 javadoc 中添加什么?
    • @PeterLawrey 我不确定是否应该在我的一个 SO 问题 here 中使用 ThreadLocal。想看看能不能帮忙。我还没有收到任何回复,我很困惑使用ThreadLocal 是正确的选择还是有更好的方法。
    猜你喜欢
    • 2016-04-25
    • 1970-01-01
    • 1970-01-01
    • 2019-07-17
    • 1970-01-01
    • 2020-04-13
    • 2012-01-13
    • 1970-01-01
    • 2012-04-18
    相关资源
    最近更新 更多