2012-07-06 15 views
1

關於類java.lang.ThreadLocal的javadoc讓我感到困惑。他們說每個訪問線程局部變量的線程都有自己的,獨立初始化的變量副本。 這裏是誰證明了在一個線程局部變量舉辦變量可以被多個線程共享一個例子(不是真正的生活中的例子):java.lang.ThreadLocal - 每個線程如何擁有自己獨立初始化的變量副本?

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將被拋出,因爲由thread1thread2共享的MY_TEST_LIST。 (正如我們所看到的,當線程1和線程叫ThreadLocalExampleset(MY_TEST_LIST)方法,即所謂的ThreadLocal變量的設置方法,它沒有創造MY_TEST_LIST的獨立本地副本)

如果有人已經問過這個問題,請給出答案的鏈接,因爲我在搜索google時沒有發現任何有趣的內容。

Regards,

+2

''ThreadLocal ''可以被看作是一個'Map ',帶有一些額外的方法來控制初始化。 – OldCurmudgeon 2012-07-06 10:30:38

回答

5

一切都很好。 ThreadLocal中的變量保持是線程本地的。在你的情況下,它是一個參考是本地的,而不是列表本身。每個線程都有自己的引用副本,但所有這些引用都指向相同的位置。換句話說:每個線程都可以保留對不同List的引用,但在您的情況下,它們都指向同一個引用。

如果你想你的榜樣工作,每個ThreadLocal應指向不同ArrayList(複印件):

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

ThreadLocal一切都指向同一個對象並沒有太大的意義,因爲在這你只需要一個全球可用的參考。

+0

爲什麼他們不把這些'List'稱爲'ListRef'?我想知道...'new'是你需要消除歧義的東西,我的意思是簡單的英語。它可以說實際上並不符合Java保存的命名約定,即使它是歷史的。 – n611x007 2014-06-27 10:46:30

2

您在兩個線程局部變量中存儲了相同的列表引用。這並不意味着ThreadLocal沒有每個線程的值。您的測試應該啓動一個線程,將某些東西存儲到線程本地,然後啓動另一個線程,查看線程局部變量是否包含任何內容(並且不會)。

這就好比你有兩個地圖,並在兩個地圖中存儲相同的列表。顯然,如果您修改存儲在一張地圖中的列表,則存儲在另一張地圖中的列表將被修改,因爲它是相同的列表。但清除一張地圖並不能清除其他地圖。

1

ThreadLocal不會強制每個線程擁有不同的引用,它允許每個線程都可能擁有自己的副本,具體取決於您如何設置它。

就像每個對象都有自己的字段一樣,您仍然可以將每個對象中的該字段設置爲相同的值,或者可以將值設置爲不同。

可能值得閱讀java.lang.Threads類的源代碼以查看ThreadLocal是如何實際實現的。

+0

我完全同意你的看法!但我認爲它在javadoc中沒有很好的解釋。開發人員必須閱讀java.lang.ThreadLocal的源代碼才能知道它是如何工作的。 – mabbas 2012-07-06 13:16:55

+0

如果您只需要知道如何使用它,但不知道它是如何工作的,那麼您將爲javadoc添加什麼? – 2012-07-06 13:19:01

+0

@PeterLawrey我不確定我是否應該在我的一個SO問題[這裏]中使用'ThreadLocal'(http://stackoverflow.com/questions/37125694/initialize-socket-timeout-value-in-httpclient-and - 然後使用,它與 - resttemplate)。想看看你能不能幫忙。我還沒有收到任何迴應,我很困惑是否使用'ThreadLocal'是正確的選擇或者有更好的方法。 – user1950349 2016-05-10 21:41:12