天增的博客
首页
博客
  • 分布式解决方案
  • Java并发工具包
  • redis
  • LeetCode
  • 系统设计
  • JVM体系
Github (opens new window)
Rss (opens new window)
  • zh-CN
  • en-US
首页
博客
  • 分布式解决方案
  • Java并发工具包
  • redis
  • LeetCode
  • 系统设计
  • JVM体系
Github (opens new window)
Rss (opens new window)
  • zh-CN
  • en-US
  • Java并发工具包
  • 并发基础
    • 线程基础
      • Thread的状态
      • 进程与线程
      • 正确停止线程的方式
      • Thread的实现方式
      • waitnotifynotifyAll
      • 生产者消费者模型
    • 线程安全
      • 线程不安全
      • 线程安全
      • 需要注意线程安全问题的情况
  • 并发工具
    • 线程协作
      • Semaphore信号量
      • CountDownLatch详解
      • 使用CompletableFuture解决旅游平台问题
      • 使用CyclicBarrier解决团建问题
    • Future
      • Future主要功能
      • FutureTask源码分析
    • ThreadLocal
      • ThreadLocal内存泄漏
      • ThreadLocal使用场景
        • 保存线程不安全的工具类
        • 传递全局变量
        • 面试题: ThreadLocal是用来解决共享资源的多线程访问吗?
    • 原子类
      • 原子类的作用概览
      • 原子类的性能分析
    • 阻塞队列
      • 常见的阻塞队列
      • 阻塞队列的常用方法
      • 什么是阻塞队列
    • 并发容器
      • HashMap
      • CopyOnWriteArrayList
      • ConcurrentHashMap详解
    • 线程池
      • 为什么多线程会带来性能问题
      • 线程池的优势
      • 创建线程池的参数
        • 如何设置线程数
      • 线程池线程复用原理
      • ForkJoin框架
    • 各种锁
      • 锁的种类和特点
        • 公平锁非公平锁
        • 自旋锁非自旋锁
        • 共享锁独占锁
        • 乐观锁和悲观锁
      • JVM锁优化
      • synchronized和Lock的对比
      • lock的常用方法
  • 底层原理
    • CAS原理
    • AQS框架
    • 伪共享
    • java内存模型
      • Java内存模型介绍
      • happens-before规则
  • topic
  • Java并发工具包
  • 并发工具
  • ThreadLocal
  • ThreadLocal使用场景
2022-04-21
目录
保存线程不安全的工具类
传递全局变量
面试题: ThreadLocal是用来解决共享资源的多线程访问吗?
ThreadLocal 和 synchronized 是什么关系

ThreadLocal使用场景

# ThreadLocal使用场景

在业务开发中有两种典型的使用场景。

  1. 保存线程不安全的对象。
  2. 传递全局变量

# 保存线程不安全的工具类

典型的类: SimpleDateFormat

如果有多个线程同时调用下面这个方法,由于dateFormat是局部变量,不会有线程安全问题;但是会创建出大量的SimpleDateFormat对象,造成频繁的GC。

public String date() {
        SimpleDateFormat dateFormat = new SimpleDateFormat("mm:ss");
        return dateFormat.format(new Date());
 }

如果我们声明成全局变量呢?

static SimpleDateFormat dateFormat = new SimpleDateFormat("mm:ss");
public String date(){
   return dateFormat.format(new Date());
}

在多个线程访问下,不同的线程都是指向同一个对象,会有线程安全的问题、

用传统的解决方法: 加锁

static SimpleDateFormat dateFormat = new SimpleDateFormat("mm:ss");
public synchronized String date(){
   return dateFormat.format(new Date());
}

加锁的方式的确能够解决线程不安全的问题,但是也带来了性能低下。

正确的方式是采用ThreadLocal来创建对象

public synchronized String date(){
   SimpleDateFormat dateFormat = ThreadSafeFormatter.dateFormatThreadLocal.get();
   return dateFormat.format(new Date());
}
class ThreadSafeFormatter {
    public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("mm:ss");
        }
    };

}

这种创建的方式,最多创建的对象只和线程数相同。这样既高效的使用了内存,也保证了线程安全。

# 传递全局变量

每个线程内需要保存类似于全局变量的信息,可以让不同方法直接使用,避免参数传递的麻烦却不想被多线程共享。

例如,用 ThreadLocal 保存一些业务内容,这些信息在同一个线程内相同,但是不同的线程使用的业务内容是不相同的。

在线程生命周期内,都通过这个静态 ThreadLocal 实例的 get() 方法取得自己 set 过的那个对象,避免了将这个对象作为参数传递的麻烦。

案例:

  • 在分布式链路追踪中,log4j的MDC对象
  • 业务系统中,使用ThreadLocal传递用户信息

# 面试题: ThreadLocal是用来解决共享资源的多线程访问吗?

这道题的答案很明确——不是。

ThreadLocal不是用来解决共享资源问题的。虽然ThreadLocal可以用于解决多线程情况下的线程安全问题,但是资源不是共享的,而是每个线程独享过的。

面试官在忽悠你,独享资源何来的多线程访问呢?

ThreadLocal解决线程安全问题的时候,相比于使用“锁”而言,避免了竞争,采用了线程独享来进行操作。

具体而言,它可以在 initialValue 中 new 出自己线程独享的资源,而多个线程之间,它们所访问的对象本身是不共享的,自然就不存在任何并发问题。这是 ThreadLocal 解决并发问题的最主要思路。

如果变量变成了共享,则依然是线程不安全的:

	public synchronized String date(){
   SimpleDateFormat dateFormat = ThreadSafeFormatter.dateFormatThreadLocal.get();
   return dateFormat.format(new Date());
}
class ThreadSafeFormatter {
  	static SimpleDateFormat format = new SimpleDateFormat();
    public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return ThreadSafeFormatter.format;
        }
    };

}

# ThreadLocal 和 synchronized 是什么关系

当 ThreadLocal 用于解决线程安全问题的时候,也就是把一个对象给每个线程都生成一份独享的副本的,在这种场景下,ThreadLocal 和 synchronized 都可以理解为是用来保证线程安全的手段。

但是效果和实现原理不同:

  • ThreadLocal 是通过让每个线程独享自己的副本,避免了资源的竞争。
  • synchronized 主要用于临界资源的分配,在同一时刻限制最多只有一个线程能访问该资源。
最近更新
01
以 root 身份启动 transmission-daemon
12-13
02
Debian系统安装qbittorrent-nox
12-09
03
LXC Debain12安装zerotier并实现局域网自动nat转发
07-29
更多文章>
Theme by Vdoing | Copyright © 2015-2025 天增 | 苏ICP备16037388号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式