2

关于 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();
    }
}

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

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

问候,

4

3 回答 3

6

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

如果您希望您的示例起作用,则每个示例ThreadLocal都应指向不同ArrayList的(副本):

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

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

于 2012-07-06T10:26:46.747 回答
2

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

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

于 2012-07-06T10:30:41.603 回答
1

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

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

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

于 2012-07-06T11:02:20.810 回答