「Java 路线」| ThreadLocal

点赞关注,不再迷路,你的支持对我意义重大!

Hi,我是丑丑。本文 「Java 路线」| 导读 —— 他山之石,可以攻玉 已收录,这里有 Android 进阶成长路线笔记 & 博客,欢迎跟着彭丑丑一起成长。(联系方式在 GitHub)


前言

  • ThreadLocal 是解决多线程并发安全的一种解决方案,与 synchronized 有本质区别;
  • 在这篇文章里,我将献上 ThreadLocal 的使用方法 & 源码分析。如果能帮上忙,请务必点赞加关注,这真的对我非常重要。

目录

「Java 路线」| ThreadLocal_第1张图片

1. 前置知识

这篇文章的内容会涉及以下前置 / 相关知识,贴心的我都帮你准备好了,请享用~

  • 并发编程: 「Java 路线」| 并发编程的基础概念

  • 引用: 「Java 路线」| 引用类型 & Finalizer 机制

  • 哈希表: 【点赞催更】


2. 简介

2.1 定义

ThreadLocal 是一种 Thread-Specific Storage 的多线程编程模式,即:一个入口为多个线程分配不同的副本。由于线程间在某一时刻访问的并非同一个对象,这样就避免了多线程的数据共享,也就避免了并发安全问题。

2.2 使用场景

  • 以空间换时间: 相对于Synchronized 等互斥锁避免了上下文切换损耗,有助于提高吞吐量;

  • 线程级别的单例: 一般的单例对象是进程单例的,使用 ThreadLocal 可以轻松实现线程级别单例;

  • 共享参数: 如果一个模块有非常多地方需要使用同一个变量,相比于在每个方法中重复传递同一个参数,使用 ThreadLocal 作为一个全局变量也许是另一种选择方式。

2.3 编程规约

记得吗?《阿里巴巴Java开发手册》中提到过关于 ThreadLocal 的编程规约,如下所示:

  • 5.【强制】SimpleDateFormate 是线程不安全的类,一般不要定义为 static 变量,如果定义为static,必须加锁,或者使用 DateUtils 工具类。正例:
private static final ThreadLocal df = new ThreadLocal(){
    @Override
    protected DateFormat initialValue(){
        return new SimpleDateFormat("yyyy-MM-dd");
    }
};

说明: 如果是JDK8的应用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar,DateTimeFormatter 代替 SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe.

  • 15.【参考】(原文过于啰嗦,以下为笔者转述)ThreadLocal 变量建议使用 static 修饰,可以保证变量在类初始化时创建,所有类实例可以共享同一个静态变量。

提示:第 4 节 使用示例 中,ThreadLocal 变量就是使用 static 修饰的。


3. 使用介绍


4. 使用示例

作为一枚 Android 党,我就以熟悉的 Handler 机制中对 ThreadLocal 的应用作为例子:

android.os.Looper.java

public class Looper {

    1、静态 ThreadLocal 变量,所有类实例共享同一个 ThreadLocal 变量
    static final ThreadLocal sThreadLocal = new ThreadLocal();

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        
        2、设置 ThreadLocal 变量的值
        sThreadLocal.set(new Looper(quitAllowed));
    }

    public static Looper myLooper() {
        3、获取 ThreadLocal 变量的值
        return sThreadLocal.get();
    }

    public static void prepare() {
        prepare(true);
    }
    ...
}

要点如下:

  • 1、ThreadLocal 被声明为 static final变量,泛型参数为 Looper,表示 ThreadLocal 变量接受 Looper类型的值;
  • 2、Looper.prepare() 中调用ThreadLocal#set()设置 当前线程 的 Looper;
  • 3、Looper.myLooper()中调用ThreadLocal#get()获取 当前线程 的 Looper。

我们可以画出 Looper 中访问 ThreadLocal 的 Timethreads 图,不同线程访问的是不同的 Looper 对象,线程间不存在共享资源


5. 源码分析


6. 总结


参考资料

  • 《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》 —— 周志明 著
  • 《Java并发编程的艺术》 —— 方腾飞,魏鹏,程晓明 著
  • 《数据结构与算法分析 — Java语言描述》 [美]Mark Allen Weiss 著
  • 《阿里巴巴Java开发手册》 杨冠宝 编著

创作不易,你的「三连」是丑丑最大的动力,我们下次见!

你可能感兴趣的:(「Java 路线」| ThreadLocal)