《面试1v1》ThreadLocal

这篇具有很好参考价值的文章主要介绍了《面试1v1》ThreadLocal。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

我是 javapub,一名 Markdown 程序员从👨‍💻,八股文种子选手。

面试官: 你好,请问你对 ThreadLocal 有了解吗?

候选人: 您好,我知道 ThreadLocal 是一个 Java 中的类,它可以让每个线程都拥有自己的变量副本,从而避免了线程安全问题。

面试官: 非常好,那你能否详细介绍一下 ThreadLocal 的使用方法?

候选人: 当然可以。ThreadLocal 的使用方法非常简单,我们只需要创建一个 ThreadLocal 对象,然后调用它的 set 方法来设置当前线程的变量值,调用 get 方法来获取当前线程的变量值即可。下面是一个简单的示例代码:

public class ThreadLocalDemo {
    private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            threadLocal.set("Hello from thread1");
            System.out.println(threadLocal.get());
        });

        Thread thread2 = new Thread(() -> {
            threadLocal.set("Hello from thread2");
            System.out.println(threadLocal.get());
        });

        thread1.start();
        thread2.start();
    }
}

这个示例代码中,我们创建了一个 ThreadLocal 对象,并在两个线程中分别设置了不同的变量值。由于每个线程都有自己的变量副本,所以这两个线程互不干扰,输出的结果也是不同的。

面试官: 非常好,那你能否解释一下 ThreadLocal 的原理是什么?

候选人: 当然可以。ThreadLocal 的原理其实很简单,它是通过一个 ThreadLocalMap 对象来存储每个线程的变量副本的。当我们调用 ThreadLocal 的 set 方法时,实际上是在当前线程的 ThreadLocalMap 对象中存储了一个键值对,其中键是当前 ThreadLocal 对象,值是我们设置的变量值。当我们调用 ThreadLocal 的 get 方法时,实际上是在当前线程的 ThreadLocalMap 对象中查找当前 ThreadLocal 对象对应的变量值。

下面是 ThreadLocalMap 的源码实现,我在代码中加了注释,希望能够帮助您更好地理解:

class ThreadLocalMap {
    static class Entry extends WeakReference<ThreadLocal<?>> {
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }

    // 初始容量为 16
    private static final int INITIAL_CAPACITY = 16;

    // 扩容因子为 2
    private static final float LOAD_FACTOR = 0.75f;

    // 存储键值对的数组
    private Entry[] table;

    // 数组中键值对的数量
    private int size = 0;

    // 下一个要清理的键值对的索引
    private int threshold;

    // 清理键值对的阈值
    private void setThreshold(int len) {
        threshold = (int) (len * LOAD_FACTOR);
    }

    // 获取键值对的值
    private Object getEntry(ThreadLocal<?> key) {
        int i = key.threadLocalHashCode & (table.length - 1);
        Entry e = table[i];
        if (e != null && e.get() == key) {
            return e.value;
        } else {
            return null;
        }
    }

    // 设置键值对的值
    private void setEntry(ThreadLocal<?> key, Object value) {
        // 清理键值对
        expungeStaleEntries();

        // 计算键值对的索引
        int i = key.threadLocalHashCode & (table.length - 1);

        // 如果该位置已经有键值对了,则往后查找空位置
        for (Entry e = table[i]; e != null; e = table[i = nextIndex(i, table.length)]) {
            ThreadLocal<?> k = e.get();

            // 如果找到了相同的 ThreadLocal 对象,则直接替换值
            if (k == key) {
                e.value = value;
                return;
            }

            // 如果找到了一个空的位置,则插入新的键值对
            if (k == null) {
                replaceStaleEntry(key, value, i);
                return;
            }
        }

        // 如果该位置没有键值对,则插入新的键值对
        table[i] = new Entry(key, value);
        int sz = ++size;
        if (sz >= threshold) {
            // 扩容
            rehash();
        }
    }

    // 清理过期的键值对
    private void expungeStaleEntries() {
        Entry[] tab = table;
        int len = tab.length;
        for (int i = 0; i < len; i++) {
            Entry e = tab[i];
            if (e != null && e.get() == null) {
                // 清理过期的键值对
                expungeStaleEntry(i);
            }
        }
    }

    // 清理指定位置的键值对
    private void 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;
                }
            }
        }
    }

    // 扩容
    private void rehash() {
        expungeStaleEntries();

        // 如果当前数组长度已经达到最大值,则不再扩容
        if (size >= threshold - threshold / 4) {
            return;
        }

        int newCapacity = table.length * 2;
        Entry[] newTable = new Entry[newCapacity];
        int count = 0;

        for (Entry e : table) {
            if (e != null) {
                ThreadLocal<?> k = e.get();
                if (k == null) {
                    e.value = null;
                } else {
                    int i = k.threadLocalHashCode & (newCapacity - 1);
                    while (newTable[i] != null) {
                        i = nextIndex(i, newCapacity);
                    }
                    newTable[i] = e;
                    count++;
                }
            }
        }

        setThreshold(newCapacity);
        size = count;
        table = newTable;
    }

    // 计算下一个索引
    private static int nextIndex(int i, int len) {
        return (i + 1) % len;
    }
}

面试官: 非常好,那你能否解释一下 ThreadLocal 的优缺点是什么?

候选人: 当然可以。ThreadLocal 的优点是它可以让每个线程都拥有自己的变量副本,从而避免了线程安全问题。另外,ThreadLocal 的使用方法非常简单,只需要调用 set 和 get 方法即可。

ThreadLocal 的缺点是它可能会导致内存泄漏问题。由于每个线程都有自己的变量副本,如果我们没有及时清理这些变量副本,就可能会导致内存泄漏。另外,ThreadLocal 的使用也可能会导致上下文切换的开销增加,因为每个线程都需要维护自己的变量副本。

面试官: 非常好,你对 ThreadLocal 的了解非常深入,今天就到这里吧。

候选人: 谢谢您的提问,我很高兴能够分享我的知识。

《面试1v1》ThreadLocal

最近我在更新《面试1v1》系列文章,主要以场景化的方式,讲解我们在面试中遇到的问题,致力于让每一位工程师拿到自己心仪的offer,感兴趣可以关注JavaPub追更!

🎁目录合集:

Gitee:https://gitee.com/rodert/JavaPub

GitHub:https://github.com/Rodert/JavaPub

http://javapub.net.cn文章来源地址https://www.toymoban.com/news/detail-463251.html

到了这里,关于《面试1v1》ThreadLocal的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 《面试1v1》ElasticSearch基础

    🍅 作者简介:王哥,CSDN2022博客总榜Top100🏆、博客专家💪 🍅 技术交流:定期更新Java硬核干货,不定期送书活动 🍅 王哥多年工作总结:Java学习路线总结, 点击 突击面试 🍅 数十万人的面试选择: 面试说人话系列《面试1v1》 我是 javapub,一名 Markdown 程序员从👨‍💻,

    2024年02月12日
    浏览(37)
  • 《面试1v1》java多线程

    我是 javapub,一名 Markdown 程序员从👨‍💻,八股文种子选手。 面试官 : 说说你对多线程的理解? 候选人: 多线程就是同时运行多个线程,实现一件事的并行处理。比如开个程序,同时下载多个文件,同时处理多个客户端请求等等。 面试官 :那什么是线程安全的?举个例子? 候选

    2024年02月02日
    浏览(47)
  • 《面试1v1》ElasticSearch倒排索引

    🍅 作者简介:王哥,CSDN2022博客总榜Top100🏆、博客专家💪 🍅 技术交流:定期更新Java硬核干货,不定期送书活动 🍅 王哥多年工作总结:Java学习路线总结, 点击 突击面试 🍅 数十万人的面试选择: 面试说人话系列《面试1v1》 我是 javapub,一名 Markdown 程序员从👨‍💻,

    2024年02月13日
    浏览(41)
  • 《面试1v1》ElasticSearch架构设计

    🍅 作者简介:王哥,CSDN2022博客总榜Top100🏆、博客专家💪 🍅 技术交流:定期更新Java硬核干货,不定期送书活动 🍅 王哥多年工作总结:Java学习路线总结, 点击 突击面试 🍅 数十万人的面试选择: 面试说人话系列《面试1v1》 我是 javapub,一名 Markdown 程序员从👨‍💻,

    2024年02月14日
    浏览(47)
  • 《面试1v1》Redis主从架构

    🍅 作者简介:王哥,CSDN2022博客总榜Top100🏆、博客专家💪 🍅 技术交流:定期更新Java硬核干货,不定期送书活动 🍅 王哥多年工作总结:Java学习路线总结, 点击 突击面试 🍅 数十万人的面试选择: 面试说人话系列《面试1v1》 我是 javapub,一名 Markdown 程序员从👨‍💻,

    2024年02月11日
    浏览(47)
  • 《面试1v1》ElasticSearch 和 Lucene

    🍅 作者简介:王哥,CSDN2022博客总榜Top100🏆、博客专家💪 🍅 技术交流:定期更新Java硬核干货,不定期送书活动 🍅 王哥多年工作总结:Java学习路线总结, 点击 突击面试 🍅 数十万人的面试选择: 面试说人话系列《面试1v1》 我是 javapub,一名 Markdown 程序员从👨‍💻,

    2024年02月14日
    浏览(46)
  • 《面试1v1》大厂的Kafka使用场景

    🍅 作者简介:王哥,CSDN2022博客总榜Top100🏆、博客专家💪 🍅 技术交流:定期更新Java硬核干货,不定期送书活动 🍅 王哥多年工作总结:Java学习路线总结, 点击 突击面试 🍅 数十万人的面试选择: 面试说人话系列《面试1v1》 我是 javapub,一名 Markdown 程序员从👨‍💻,

    2024年02月16日
    浏览(41)
  • 《面试1v1》Kafka的ack机制

    🍅 作者简介:王哥,CSDN2022博客总榜Top100🏆、博客专家💪 🍅 技术交流:定期更新Java硬核干货,不定期送书活动 🍅 王哥多年工作总结:Java学习路线总结, 点击 突击面试 🍅 数十万人的面试选择: 面试说人话系列《面试1v1》 我是 javapub,一名 Markdown 程序员从👨‍💻,

    2024年02月14日
    浏览(36)
  • 《面试1v1》G1垃圾回收器

    我是 javapub,一名 Markdown 程序员从👨‍💻,八股文种子选手。 面试官 : G1垃圾收集器?听说很牛逼的样子! 候选人: 是的,G1是JDK9默认的垃圾收集器,代替了CMS收集器。它的目标是达到更高的吞吐量和更短的GC停顿时间。 面试官 : 听你一说,我就不高兴了!G1到底好在哪儿? 候选

    2024年02月08日
    浏览(49)
  • 《面试1v1》Kafka与传统消息系统区别

    🍅 作者简介:王哥,CSDN2022博客总榜Top100🏆、博客专家💪 🍅 技术交流:定期更新Java硬核干货,不定期送书活动 🍅 王哥多年工作总结:Java学习路线总结, 点击 突击面试 🍅 数十万人的面试选择: 面试说人话系列《面试1v1》 我是 javapub,一名 Markdown 程序员从👨‍💻,

    2024年02月15日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包