java.lang.ThreadLocal - 每个Thread如何拥有自己独立初始化的变量副本?

时间:2012-07-06 10:20:19

标签: java thread-local

关于类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.IndexOutOfBoundsExceptionMY_TEST_LIST共享thread1,系统会引发thread2。 (正如我们所看到的,当thread1和thread2调用set(MY_TEST_LIST) ThreadLocalExample方法调用ThreadLocal变量的set方法时,它没有创建一个独立的本地副本MY_TEST_LIST

如果有人已经提出这个问题,请提供答案链接,因为我在谷歌研究时没有发现任何有趣的内容。

此致

3 个答案:

答案 0 :(得分:6)

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

如果您希望自己的示例有效,则每个ThreadLocal应指向不同的ArrayList(副本):

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

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

答案 1 :(得分:2)

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

如果您有两张地图,那就完全一样,并在两张地图中存储相同的列表。显然,如果修改存储在一个地图中的列表,则会修改存储在另一个地图中的列表,因为它是相同的列表。但清除一张地图并不能清除另一张地图。

答案 2 :(得分:1)

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

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

可能值得阅读java.lang.Threads类的源代码,以了解ThreadLocal的实际实现方式。