第一次见到要主动降薪的。。。

大家好!我是鸭鸭!

大家有没有在工作中收到过什么暗示?

鸭鸭今天刷到一个帖子,楼主是美团 L7,年包 80w,被暗示 cr 过高,自觉年纪大了不好跳槽,担心收到大礼包,希望通过申请降薪,留住这份工作:

虽然这两年大家已经普遍接受了大环境不行,但鸭鸭还是第一次见 L7 考虑主动降薪保住工作。

看评论区,也有不少朋友劝楼主不要主动提降薪,可以做两手准备,比如准备面试试试水,看看机会。

降薪是一时的妥协,并不一定能带来想要的结果,大家面对这样的问题时,一定要慎之又慎地做决策。

鸭鸭也看到了楼主的回复,看起来他也认真听取了建议并开始考虑转型(bushi)。

生活不易,鸭鸭叹气。不知道面对现在的大环境,大家的职业发展有什么变化吗?

......

如今的就业市场虽然艰难,但依然存在不少机会,想要不错过这些机会,还是得多多准备,总不能面试的时候连八股文都答不出来。

来看看鸭鸭今天为大家准备的面试题吧!

为什么在 Java 中需要使用 ThreadLocal?

回答重点

因为在多线程编程中,多个线程可能会同时访问和修改共享变量,导致线程安全问题。ThreadLocal 提供了一种简单的解决方案,使每个线程都有自己的独立变量副本,避免了多线程间的变量共享和竞争,从而解决了线程安全问题。

与通过加锁、同步块等传统方式来保证线程安全相比。ThreadLocal 不需要对变量访问进行同步,减少了上下文切换、锁竞争的性能损耗。

扩展知识

常见应用场景

  • 数据库连接管理:每个线程拥有自己的数据库连接,避免了多个线程共享同一个连接导致的线程安全问题。
  • 用户上下文管理:在处理用户请求时,每个线程拥有独立的用户上下文(如用户ID、Session信息),在并发环境中确保正确的用户数据。

ThreadLocal 的原理

ThreadLocal 通过为每个线程创建一个独立的变量副本来实现线程本地化存储。ThreadLocal 实际上是为每个线程创建了一个 ThreadLocalMap,而 ThreadLocalMap 是每个线程内部持有的结构。

ThreadLocalMap 的键是 ThreadLocal 对象,而值则是线程独立的变量副本。当线程访问 ThreadLocal.get() 时,它会根据当前线程在自己的 ThreadLocalMap 中找到对应的变量副本。

以下是一个简化的访问流程:

  • 线程A访问 ThreadLocal.get() 时,从 ThreadLocalMap 中找到与该 ThreadLocal 对象对应的值。
  • 线程B访问 ThreadLocal.get() 时,它有自己独立的 ThreadLocalMap,获取的是与其自身相关的值,互不干扰。

ThreadLocal 通俗理解

最近不是开放三胎政策嘛,假设你有三个孩子。

现在你带着三个孩子出去逛街,路过了玩具店,三个孩子都看中了一款变形金刚。

所以你买了一个变形金刚,打算让三个孩子轮着玩。

回到家你发现,孩子因为这个玩具吵架了,三个都争着要玩,谁也不让着谁。

这时候怎么办呢?你可以去拉架,去讲道理,说服孩子轮流玩,但这很累。

所以一个简单的办法就是出去再买两个变形金刚,这样三个孩子都有各自的变形金刚,世界就暂时得到了安宁。

映射到我们今天的主题,变形金刚就是共享变量,孩子就是程序运行的线程。有多个线程(孩子),争抢同一个共享变量(玩具),就会产生冲突,而程序的解决办法是加锁(父母说服,讲道理,轮流玩),但加锁就意味着性能的消耗(父母比较累)。

所以有一种解决办法就是避免共享(让每个孩子都各自拥有一个变形金刚),这样线程之间就不需要竞争共享变量(孩子之间就不会争抢)。

所以为什么需要 ThreadLocal?

就是为了通过本地化资源来避免共享,避免了多线程竞争导致的锁等消耗。

这里需要强调一下,不是说任何东西都能直接通过避免共享来解决,因为有些时候就必须共享。

举个例子:当利用多线程同时累加一个变量的时候,此时就必须共享,因为一个线程的对变量的修改需要影响要另个线程,不然累加的结果就不对了。

再举个不需要共享的例子:比如现在每个线程需要判断当前请求的用户来进行权限判断,那这个用户信息其实就不需要共享,因为每个线程只需要管自己当前执行操作的用户信息,跟别的用户不需要有交集。

好了,道理很简单,这下子想必你已经清晰了 ThreadLocal 出现的缘由了。

代码示例

public class ThreadLocalExample {
    // 定义一个 ThreadLocal,用来保存每个线程独立的变量副本
    private static final ThreadLocal<Integer> threadLocalCounter = ThreadLocal.withInitial(() -> 0);

    public static void main(String[] args) {
        // 创建三个线程,每个线程都会有自己独立的变量副本
        Thread t1 = new Thread(() -> {
            incrementAndPrint();
        });

        Thread t2 = new Thread(() -> {
            incrementAndPrint();
        });

        Thread t3 = new Thread(() -> {
            incrementAndPrint();
        });

        // 启动线程
        t1.start();
        t2.start();
        t3.start();
    }

    private static void incrementAndPrint() {
        for (int i = 0; i < 5; i++) {
            int currentValue = threadLocalCounter.get();
            threadLocalCounter.set(currentValue + 1);
            System.out.println(Thread.currentThread().getName() + " : " + threadLocalCounter.get());
        }
    }
}

在上面的例子中,三个线程各自操作 ThreadLocal 提供的变量副本,互不干扰,解决了多线程之间的共享数据竞争问题。

最后

再来推荐下我们的面试刷题网站和小程序:面试鸭

相关推荐
南宫生5 分钟前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石13 分钟前
12/21java基础
java
李小白6621 分钟前
Spring MVC(上)
java·spring·mvc
sanguine__21 分钟前
Web APIs学习 (操作DOM BOM)
学习
GoodStudyAndDayDayUp34 分钟前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea
装不满的克莱因瓶1 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
n北斗1 小时前
常用类晨考day15
java
骇客野人1 小时前
【JAVA】JAVA接口公共返回体ResponseData封装
java·开发语言
GISer_Jing2 小时前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试
m0_748245522 小时前
吉利前端、AI面试
前端·面试·职场和发展