大家好!我是鸭鸭!
大家有没有在工作中收到过什么暗示?
鸭鸭今天刷到一个帖子,楼主是美团 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
提供的变量副本,互不干扰,解决了多线程之间的共享数据竞争问题。
最后
再来推荐下我们的面试刷题网站和小程序:面试鸭!