0

0

什么是线程局部变量?

零下一度

零下一度

发布时间:2017-07-21 14:34:26

|

2547人浏览过

|

来源于php中文网

原创

threadlocal翻译成中文比较准确的叫法应该是:线程局部变量。

  这个玩意有什么用处,或者说为什么要有这么一个东东?先解释一下,在并发编程的时候,成员变量如果不做任何处理其实是线程不安全的,各个线程都在操作同一个变量,显然是不行的,并且我们也知道volatile这个关键字也是不能保证线程安全的。那么在有一种情况之下,我们需要满足这样一个条件:变量是同一个,但是每个线程都使用同一个初始值,也就是使用同一个变量的一个新的副本。这种情况之下ThreadLocal就非常使用,比如说DAO的数据库连接,我们知道DAO是单例的,那么他的属性Connection就不是一个线程安全的变量。而我们每个线程都需要使用他,并且各自使用各自的。这种情况,ThreadLocal就比较好的解决了这个问题。

  我们从源码的角度来分析这个问题。

  首先定义一个ThreadLocal:

   ThreadLocal tl =  ThreadLocal  Connection initConn = = DriverManager.getConnection("url, name and password"=( ==

 

package java.lang;import java.lang.ref.*;import java.util.concurrent.atomic.AtomicInteger;public class ThreadLocal {   private final int threadLocalHashCode = nextHashCode();   private static AtomicInteger nextHashCode =new AtomicInteger();private static final int HASH_INCREMENT = 0x61c88647;private static int nextHashCode() {return nextHashCode.getAndAdd(HASH_INCREMENT);
    }protected T initialValue() {return null;
    }public ThreadLocal() {
    }public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);if (e != null)return (T)e.value;
        }return setInitialValue();
    }private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);if (map != null)
            map.set(this, value);elsecreateMap(t, value);return value;
    }public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);if (map != null)
            map.set(this, value);elsecreateMap(t, value);
    } public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());         if (m != null)
             m.remove(this);
     }ThreadLocalMap getMap(Thread t) {return t.threadLocals;
    }void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {return new ThreadLocalMap(parentMap);
    }T childValue(T parentValue) {throw new UnsupportedOperationException();
    }static class ThreadLocalMap {static class Entry extends WeakReference {/** The value associated with this ThreadLocal. */Object value;

            Entry(ThreadLocal k, Object v) {super(k);
                value = v;
            }
        }private static final int INITIAL_CAPACITY = 16;private Entry[] table;private int size = 0;private int threshold; // Default to 0private void setThreshold(int len) {
            threshold = len * 2 / 3;
        }private static int nextIndex(int i, int len) {return ((i + 1 < len) ? i + 1 : 0);
        }private static int prevIndex(int i, int len) {return ((i - 1 >= 0) ? i - 1 : len - 1);
        }ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];if (e != null) {
                    ThreadLocal key = e.get();if (key != null) {
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);int h = key.threadLocalHashCode & (len - 1);while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }private Entry getEntry(ThreadLocal key) {int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];if (e != null && e.get() == key)return e;elsereturn getEntryAfterMiss(key, i, e);
        }private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {
            Entry[] tab = table;int len = tab.length;while (e != null) {
                ThreadLocal k = e.get();if (k == key)return e;if (k == null)
                    expungeStaleEntry(i);elsei = nextIndex(i, len);
                e = tab[i];
            }return null;
        }private void set(ThreadLocal key, Object value) {Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (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) && sz >= threshold)
                rehash();
        }private void remove(ThreadLocal key) {
            Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (len-1);for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);return;
                }
            }
        }private void replaceStaleEntry(ThreadLocal key, Object value,                                       int staleSlot) {
            Entry[] tab = table;int len = tab.length;
            Entry e;int slotToExpunge = staleSlot;for (int i = prevIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = prevIndex(i, len))if (e.get() == null)
                    slotToExpunge = i;for (int i = nextIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
                ThreadLocal k = e.get();if (k == key) {
                    e.value = value;

                    tab[i] = tab[staleSlot];
                    tab[staleSlot] = e;if (slotToExpunge == staleSlot)
                        slotToExpunge = i;
                    cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);return;
                }if (k == null && slotToExpunge == staleSlot)
                    slotToExpunge = i;
            }tab[staleSlot].value = null;
            tab[staleSlot] = new Entry(key, value);if (slotToExpunge != staleSlot)
                cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
        }private int expungeStaleEntry(int staleSlot) {
            Entry[] tab = table;int len = tab.length;tab[staleSlot].value = null;
            tab[staleSlot] = null;
            size--;            Entry e;int i;for (i = nextIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
                ThreadLocal k = e.get();if (k == null) {
                    e.value = null;
                    tab[i] = null;
                    size--;
                } else {int h = k.threadLocalHashCode & (len - 1);if (h != i) {
                        tab[i] = null;while (tab[h] != null)
                            h = nextIndex(h, len);
                        tab[h] = e;
                    }
                }
            }return i;
        }private boolean cleanSomeSlots(int i, int n) {boolean removed = false;
            Entry[] tab = table;int len = tab.length;do {
                i = nextIndex(i, len);
                Entry e = tab[i];if (e != null && e.get() == null) {
                    n = len;
                    removed = true;
                    i = expungeStaleEntry(i);
                }
            } while ( (n >>>= 1) != 0);return removed;
        }private void rehash() {
            expungeStaleEntries();// Use lower threshold for doubling to avoid hysteresisif (size >= threshold - threshold / 4)
                resize();
        }private void resize() {
            Entry[] oldTab = table;int oldLen = oldTab.length;int newLen = oldLen * 2;
            Entry[] newTab = new Entry[newLen];int count = 0;for (int j = 0; j < oldLen; ++j) {
                Entry e = oldTab[j];if (e != null) {
                    ThreadLocal k = e.get();if (k == null) {
                        e.value = null; // Help the GC} else {int h = k.threadLocalHashCode & (newLen - 1);while (newTab[h] != null)
                            h = nextIndex(h, newLen);
                        newTab[h] = e;
                        count++;
                    }
                }
            }

            setThreshold(newLen);
            size = count;
            table = newTab;
        }private void expungeStaleEntries() {
            Entry[] tab = table;int len = tab.length;for (int j = 0; j < len; j++) {
                Entry e = tab[j];if (e != null && e.get() == null)
                    expungeStaleEntry(j);
            }
        }
    }
}

这样子,都是用同一个连接,但是每个连接都是新的,是同一个连接的副本。

  那么实现机制是如何的呢?

1、每个Thread对象内部都维护了一个ThreadLocalMap这样一个ThreadLocal的Map,可以存放若干个ThreadLocal

万兴喵影
万兴喵影

国产剪辑神器

下载
/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null;

2、当我们在调用get()方法的时候,先获取当前线程,然后获取到当前线程的ThreadLocalMap对象,如果非空,那么取出ThreadLocal的value,否则进行初始化,初始化就是将initialValue的值set到ThreadLocal中。

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);if (e != null)return (T)e.value;
    }return setInitialValue();
}

 

3、当我们调用set()方法的时候,很常规,就是将值设置进ThreadLocal中。

4、总结:当我们调用get方法的时候,其实每个当前线程中都有一个ThreadLocal。每次获取或者设置都是对该ThreadLocal进行的操作,是与其他线程分开的。

5、应用场景:当很多线程需要多次使用同一个对象,并且需要该对象具有相同初始化值的时候最适合使用ThreadLocal。

6、其实说再多也不如看一下源码来得清晰。如果要看源码,其中涉及到一个WeakReference和一个Map,这两个地方需要了解下,这两个东西分别是a.Java的弱引用,也就是GC的时候会销毁该引用所包裹(引用)的对象,这个threadLocal作为key可能被销毁,但是只要我们定义成他的类不卸载,tl这个强引用就始终引用着这个ThreadLocal的,永远不会被gc掉。b.和HashMap差不多。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
go语言 注释编码
go语言 注释编码

本专题整合了go语言注释、注释规范等等内容,阅读专题下面的文章了解更多详细内容。

2

2026.01.31

go语言 math包
go语言 math包

本专题整合了go语言math包相关内容,阅读专题下面的文章了解更多详细内容。

1

2026.01.31

go语言输入函数
go语言输入函数

本专题整合了go语言输入相关教程内容,阅读专题下面的文章了解更多详细内容。

1

2026.01.31

golang 循环遍历
golang 循环遍历

本专题整合了golang循环遍历相关教程,阅读专题下面的文章了解更多详细内容。

0

2026.01.31

Golang人工智能合集
Golang人工智能合集

本专题整合了Golang人工智能相关内容,阅读专题下面的文章了解更多详细内容。

1

2026.01.31

2026赚钱平台入口大全
2026赚钱平台入口大全

2026年最新赚钱平台入口汇总,涵盖任务众包、内容创作、电商运营、技能变现等多类正规渠道,助你轻松开启副业增收之路。阅读专题下面的文章了解更多详细内容。

76

2026.01.31

高干文在线阅读网站大全
高干文在线阅读网站大全

汇集热门1v1高干文免费阅读资源,涵盖都市言情、京味大院、军旅高干等经典题材,情节紧凑、人物鲜明。阅读专题下面的文章了解更多详细内容。

73

2026.01.31

无需付费的漫画app大全
无需付费的漫画app大全

想找真正免费又无套路的漫画App?本合集精选多款永久免费、资源丰富、无广告干扰的优质漫画应用,涵盖国漫、日漫、韩漫及经典老番,满足各类阅读需求。阅读专题下面的文章了解更多详细内容。

67

2026.01.31

漫画免费在线观看地址大全
漫画免费在线观看地址大全

想找免费又资源丰富的漫画网站?本合集精选2025-2026年热门平台,涵盖国漫、日漫、韩漫等多类型作品,支持高清流畅阅读与离线缓存。阅读专题下面的文章了解更多详细内容。

19

2026.01.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
HTML教程
HTML教程

共500课时 | 5.2万人学习

php-src源码分析探索
php-src源码分析探索

共6课时 | 0.5万人学习

PHP自制框架
PHP自制框架

共8课时 | 0.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号