天天看點

關于threadlocal的了解

最近在哪裡接觸到了threadlocal,但是發現自己對threadlocal的了解很少,基本不知道是做什麼的。是以,處于一種學習的目的,找了很多介紹threadlocal的文章。

看了很多的部落格文章,大多都介紹了threadlocal的概念。以下給出我看了這麼多文章的對其的了解,threadlocal可以翻譯成“線程局部變量”,是用來存儲線程局部變量的地方,可以保證線程安全問題,其内部實作是ThreadLocal為每個使用該變量的線程配置設定一個獨立的變量副本。還有很多描述,和同步進行對比的,說什麼高效的解決了線程安全,巴拉巴拉的。。。類似這篇文章http://www.iteye.com/topic/1123824,其實講的還蠻好的,但還是不能解決我的疑惑。

不知道大家了解到這裡有沒有些疑惑,但我是已經很疑惑了。請看上面描述的,說的是為每個獨立線程配置設定了一個變量副本,請看清楚,是變量,而不是執行個體。在我的了解中,一個變量隻是一個引用,變量的副本就是意味着多個引用,但是指向的執行個體是同一個!!多個線程互相之間改變執行個體的狀态,怎麼可能會沒有線程安全問題呢???excuse me??

為了驗證我的猜想,我最終寫下了以下測試代碼。

public class TestThreadLocal {

    @Data
    static class Modal1 {
        private String str;
        private Integer integer;

    }

    @Test
    public void test1() throws InterruptedException {
        Modal1 modal1 = new Modal1();
        modal1.setStr("123");
        modal1.setInteger();
        ThreadLocal<Modal1> modal1ThreadLocal = new ThreadLocal<>();


        Thread testClient = new TestClient(modal1, modal1ThreadLocal);
        Thread testClient1 = new TestClient1(modal1, modal1ThreadLocal);

        testClient.start();
        testClient1.start();

        testClient1.join();
//        System.out.println(modal1);
    }

    private static class TestClient extends Thread
    {
        private Modal1 modal1;
        private ThreadLocal threadLocal;
        public TestClient(Modal1 modal1, ThreadLocal threadLocal) {
            this.modal1 = modal1;
            this.threadLocal = threadLocal;
        }
        public void run(){
            try {
                Thread.sleep(L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            threadLocal.set(modal1);
            modal1.setStr("456");
            modal1.setInteger();
            //④每個線程打出3個序列值
            for (int i = ; i < ; i++) {
                System.out.println("thread[" + Thread.currentThread().getName() +
                        "] modal1[" + threadLocal.get() + "]");
            }
        }
    }

    private static class TestClient1 extends Thread
    {
        private Modal1 modal1;
        private ThreadLocal threadLocal;
        public TestClient1(Modal1 modal1, ThreadLocal threadLocal) {
            this.modal1 = modal1;
            this.threadLocal = threadLocal;
        }
        public void run(){
            threadLocal.set(modal1);

            //④每個線程打出3個序列值
            for (int i = ; i < ; i++) {
                System.out.println("thread[" + Thread.currentThread().getName() +
                        "] modal1[" + threadLocal.get() + "]");
            }

            try {
                Thread.sleep(L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(threadLocal.get());
        }
    }
}
           

果然結果就是我想的,不會保證線程安全。

thread[Thread-] modal1[TestThreadLocal.Modal1(str=, integer=)]
thread[Thread-] modal1[TestThreadLocal.Modal1(str=, integer=)]
thread[Thread-] modal1[TestThreadLocal.Modal1(str=, integer=)]
thread[Thread-] modal1[TestThreadLocal.Modal1(str=, integer=)]
thread[Thread-] modal1[TestThreadLocal.Modal1(str=, integer=)]
thread[Thread-] modal1[TestThreadLocal.Modal1(str=, integer=)]
           

是以各位大神們,難道是我的了解那麼有問題嗎?為了弄清楚這個問題,我又拿着我的疑惑去請教公司内的一個大神。果然經過了大神的一番指導,我終于弄清楚了我的困惑。

其實好多的部落格拿同步和threadlocal比是不對的,關于我上面的問題,多個線程通路同一個執行個體産生的線程安全問題threadlocal是解決不了的,而同步是能解決這個線程安全問題的(忽略性能)。threadlocal把變量本地化解決的是線程之間變量互相引用所産生的線程安全問題。是以,兩者在這個方面是沒有可比性的!!!

而且上面我的例子對于threadlocal的使用也是錯誤的。一般會類似這樣的使用:

a = new Object();
threadlocal.set(a);
           

這樣使用的話确實是從某一方面解決了線程安全問題,但也絕對不應該拿來和同步來比較。其實辯證的來思考這件事情,線程局部變量就是為了線程所經過的所有方法都能獲得這個變量,用一種很笨的方式也可以實作相同的效果,就是所有線程經過的方法參數都加上這個變量,也是可以實作目的的,隻不過這種方式太笨而已。

綜上,threadlocal的使用還是限定在有限的場景裡,例如資料庫連結,但隻要明白它是做什麼的,解決什麼問題,有時候還是一個利器呢。