Java中ThreadLocal如何使用

這篇文章將為大家詳細講解有關(guān)Java中ThreadLocal如何使用,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

創(chuàng)新互聯(lián)公司于2013年成立,先為德宏州等服務(wù)建站,德宏州等地企業(yè),進行企業(yè)商務(wù)咨詢服務(wù)。為德宏州企業(yè)網(wǎng)站制作PC+手機+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。

從源碼入手

首先,讓我們看看 ThreadLocal 類中的介紹: >This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID). > >Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).

按照文中所述,ThreadLocal 提供的是線程本地變量,每個線程都有一份單獨的副本,經(jīng)常使用的方式是私有靜態(tài)變量。關(guān)鍵在于下一段,線程存活,ThreadLocal 實例就可以被訪問,線程消失,就會被垃圾回收。

get()方法

看到這兒,有沒有想起上一篇內(nèi)容所說的引用類型,有可能是軟引用或者弱引用,具體是什么呢?還是來看看代碼:

    public T get() {
        // 獲取當前線程
        Thread t = Thread.currentThread();
        // 獲取線程里的map
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

上面展示的是 ThreadLocal 中的get()方法,關(guān)鍵的 map 是在 Thread 類中的threadLocals變量,讓我們繼續(xù)看看 ThreadLocalMap 的源代碼:

    ThreadLocal.ThreadLocalMap threadLocals = null;

    static class ThreadLocalMap {
        static class Entry extends WeakReference<threadlocal<?>&gt; {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<!--?--> k, Object v) {
                // 使用ThreadLocal作為key,并且是弱引用
                super(k);
                value = v;
            }
        }

        // 省略代碼
    }

根據(jù)上一篇文章所述,如果一個對象只有弱引用,那么當下一次 GC 進行時,該對象就會被回收。那么讓我們整理一下:

  1. ThreadLocalMap 的 Entry 對 ThreadLocal 的引用為弱引用。

  2. ThreadLocal 本身并不存儲值,具體的 value 依舊在各個線程中。因此你可以把 ThreadLocal 看成一個工具類。

但需要注意的是,Entry 中,只有key是弱引用,但 value 依舊是強引用。那會不會出現(xiàn) key 被垃圾回收后,這個 map 的 key 為 null,但 value 依舊存在的情況呢?

set()方法

確實是有可能的,但 JDK 本身也做了優(yōu)化,可以看看 ThreadLocalMap 的 set()方法:

        private void set(ThreadLocal<!--?--> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode &amp; (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<!--?--> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) &amp;&amp; sz &gt;= threshold)
                rehash();
        }

調(diào)用 set()的時候,ThreadLocalMap 檢查到 key 為 null 的 entry 時,會將 value 也設(shè)置為 null,這樣 value 之前對應(yīng)的實例也可以被回收。

使用場景

簡單使用

先讓我們看一個簡單的例子:

public class ThreadLocalSimpleDemo {

    public static void main(String[] args) {
        int threads = 3;
        InnerClass innerClass = new InnerClass();
        for (int i = 1; i &lt;= threads; i++) {
            new Thread(() -&gt; {
                for (int j = 0; j &lt; 4; j++) {
                    innerClass.add(String.valueOf(j));
                    innerClass.print();
                }
                innerClass.set("hello world");
            }, "thread - " + i).start();
        }
    }

    private static class InnerClass {

        /**
         * 添加
         */
        public void add(String newStr) {
            StringBuilder str = Counter.counter.get();
            Counter.counter.set(str.append(newStr));
        }

        /**
         * 打印
         */
        public void print() {
            System.out.printf(
                    "Thread name:%s , ThreadLocal hashcode:%s, Instance hashcode:%s, Value:%s\n",
                    Thread.currentThread().getName(),
                    Counter.counter.hashCode(),
                    Counter.counter.get().hashCode(),
                    Counter.counter.get().toString()
            );
        }

        /**
         * 賦值
         */
        public void set(String words) {
            Counter.counter.set(new StringBuilder(words));
            System.out.printf(
                    "Set, Thread name:%s , ThreadLocal hashcode:%s,  Instance hashcode:%s, Value:%s\n",
                    Thread.currentThread().getName(),
                    Counter.counter.hashCode(),
                    Counter.counter.get().hashCode(),
                    Counter.counter.get().toString()
            );
        }
    }

    private static class Counter {
        /**
         * 初始化時是一個空的StringBuilder對象
         */
        private static ThreadLocal<stringbuilder> counter = ThreadLocal.withInitial(StringBuilder::new);
    }
}

其打印結(jié)果為:

Thread name:thread - 3 , ThreadLocal hashcode:310471657, Instance hashcode:640658548, Value:0
Thread name:thread - 2 , ThreadLocal hashcode:310471657, Instance hashcode:126253473, Value:0
Thread name:thread - 2 , ThreadLocal hashcode:310471657, Instance hashcode:126253473, Value:01
Thread name:thread - 2 , ThreadLocal hashcode:310471657, Instance hashcode:126253473, Value:012
Thread name:thread - 2 , ThreadLocal hashcode:310471657, Instance hashcode:126253473, Value:0123
Thread name:thread - 1 , ThreadLocal hashcode:310471657, Instance hashcode:829132711, Value:0
Thread name:thread - 1 , ThreadLocal hashcode:310471657, Instance hashcode:829132711, Value:01
Thread name:thread - 1 , ThreadLocal hashcode:310471657, Instance hashcode:829132711, Value:012
Thread name:thread - 1 , ThreadLocal hashcode:310471657, Instance hashcode:829132711, Value:0123
Set, Thread name:thread - 1 , ThreadLocal hashcode:310471657,  Instance hashcode:820066274, Value:hello world
Thread name:thread - 3 , ThreadLocal hashcode:310471657, Instance hashcode:640658548, Value:01
Thread name:thread - 3 , ThreadLocal hashcode:310471657, Instance hashcode:640658548, Value:012
Set, Thread name:thread - 2 , ThreadLocal hashcode:310471657,  Instance hashcode:155293473, Value:hello world
Thread name:thread - 3 , ThreadLocal hashcode:310471657, Instance hashcode:640658548, Value:0123
Set, Thread name:thread - 3 , ThreadLocal hashcode:310471657,  Instance hashcode:1804272849, Value:hello world

可以看出,我們在使用 ThreadLocal 時,用的是同一個對象,但各個線程對應(yīng)的實例是不一樣的。而在調(diào)用 set() 方法后,對應(yīng)的實例會被替換。

Session

對于 Java Web 應(yīng)用而言,Session 保存了很多信息。很多時候需要通過 Session 獲取信息,有些時候又需要修改 Session 的信息。一方面,需要保證每個線程有自己單獨的 Session 實例。另一方面,由于很多地方都需要操作 Session,存在多方法共享 Session 的需求。使用 ThreadLocal 進行實現(xiàn):

public class SessionHandler {

  public static ThreadLocal<session> session = ThreadLocal.<session>withInitial(() -&gt; new Session());

  @Data
  public static class Session {
    private String id;
    private String user;
    private String status;
  }

  public String getUser() {
    return session.get().getUser();
  }

  public String getStatus() {
    return session.get().getStatus();
  }

  public void setStatus(String status) {
    session.get().setStatus(status);
  }

  public static void main(String[] args) {
    new Thread(() -&gt; {
      SessionHandler handler = new SessionHandler();
      handler.getStatus();
      handler.getUser();
      handler.setStatus("close");
      handler.getStatus();
    }).start();
  }
}

關(guān)于Java中ThreadLocal如何使用就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

文章名稱:Java中ThreadLocal如何使用
標題URL:http://bm7419.com/article34/psdspe.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供動態(tài)網(wǎng)站App設(shè)計、營銷型網(wǎng)站建設(shè)、自適應(yīng)網(wǎng)站定制網(wǎng)站、品牌網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都做網(wǎng)站