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

大家好!我是鸭鸭!

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

鸭鸭今天刷到一个帖子,楼主是美团 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 提供的变量副本,互不干扰,解决了多线程之间的共享数据竞争问题。

最后

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

相关推荐
向阳12188 分钟前
Dubbo负载均衡
java·运维·负载均衡·dubbo
懒惰才能让科技进步11 分钟前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝
Gu Gu Study17 分钟前
【用Java学习数据结构系列】泛型上界与通配符上界
java·开发语言
测试199826 分钟前
2024软件测试面试热点问题
自动化测试·软件测试·python·测试工具·面试·职场和发展·压力测试
love_and_hope27 分钟前
Pytorch学习--神经网络--搭建小实战(手撕CIFAR 10 model structure)和 Sequential 的使用
人工智能·pytorch·python·深度学习·学习
Chef_Chen30 分钟前
从0开始学习机器学习--Day14--如何优化神经网络的代价函数
神经网络·学习·机器学习
芊寻(嵌入式)39 分钟前
C转C++学习笔记--基础知识摘录总结
开发语言·c++·笔记·学习
WaaTong41 分钟前
《重学Java设计模式》之 原型模式
java·设计模式·原型模式
m0_7430484441 分钟前
初识Java EE和Spring Boot
java·java-ee
AskHarries43 分钟前
Java字节码增强库ByteBuddy
java·后端