栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

ThreadLocal

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

ThreadLocal

1. ThreadLocal简述

通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。

如果想实现每一个线程都有自己的专属本地变量该如何解决呢? 

在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。

ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。

在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。

ThreadLocal类主要解决的就是让每个线程绑定自己的值,可以将ThreadLocal类形象的比喻成存放数据的盒子,盒子中可以存储每个线程的私有数据。

如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的本地副本,这也是ThreadLocal变量名的由来。

可以使用 get() 和 set()方法来获取默认值或将其值更改为当前线程所存的副本的值,从而避免了线程安全问题。

举个栗子:

比如有两个人去宝屋收集宝物,这两个共用一个袋子的话肯定会产生争执,但是给他们两个人每个人分配一个袋子的话就不会出现这样的问题。如果把这两个人比作线程的话,那么 ThreadLocal 就是用来避免这两个线程竞争的。


2. ThreadLocal栗子

举个栗子:

public class ThreadLocalDemo implements Runnable {

	private static final ThreadLocal FORMAT_THREAD_LOCAL = ThreadLocal.withInitial(() -> {
		SimpleDateFormat yyyyMMdd_hHmm = new SimpleDateFormat("yyyyMMdd HHmm");
		return yyyyMMdd_hHmm;
	});

	public static void main(String[] args) throws InterruptedException {
		ThreadLocalDemo obj = new ThreadLocalDemo();
		for (int i = 0; i < 10; i++) {
			Thread t = new Thread(obj, "" + i);
			Thread.sleep(new Random().nextInt(1000));
			t.start();
		}
	}

	@Override
	public void run() {
		System.out.println("Thread Name= " + Thread.currentThread().getName() + "; default Formatter = " + FORMAT_THREAD_LOCAL.get().toPattern());
		try {
			Thread.sleep(new Random().nextInt(1000));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//formatter pattern is changed here by thread, but it won't reflect to other threads
		FORMAT_THREAD_LOCAL.set(new SimpleDateFormat());
		System.out.println("Thread Name= " + Thread.currentThread().getName() + " formatter = " + FORMAT_THREAD_LOCAL.get().toPattern());
	}
}

测试结果:  

 Thread-0 已经改变了 formatter 的值,但仍然是 thread-2 默认格式化程序与初始化值相同,其他线程也一样。


3. ThreadLocal源码分析 Thread类源码:
public class Thread implements Runnable {
    //......
    //与此线程有关的ThreadLocal值。由ThreadLocal类维护
    ThreadLocal.ThreadLocalMap threadLocals = null;

    //与此线程有关的InheritableThreadLocal值。由InheritableThreadLocal类维护
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    //......
}

从上面Thread类 源代码可以看出Thread类中有一个threadLocals和 一个inyertableThreadLocals变量;

它们都是 ThreadLocalMap类型的变量,可以把 ThreadLocalMap理解为ThreadLocal类实现的定制化的HashMap。

默认情况下这两个变量都是 null,只有当前线程调用 ThreadLocal类的set或get方法时才创建它;

实际上调用这两个方法的时候,我们调用的是ThreadLocalMap类对应的 get()、set()方法。

ThreadLocal的set方法
public void set(T value) {
    //获取当前线程实例
    Thread t = Thread.currentThread();
    //以实例t为key,获取对应的线程的集合对象
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
     else
        createMap(t, value);
}

 分析上面set方法代码:

  • 最终的变量是放在了当前线程的 ThreadLocalMap 中,并不是存在 ThreadLocal 上;
  • ThreadLocal 可以理解为只是ThreadLocalMap的封装,传递了变量值。
  • ThreadLocal 类中可以通过Thread。currentThread()获取到当前线程对象后,直接通过getMap(Thread t)可以访问到该线程的ThreadLocalMap 对象。

ThreadLocalMap是ThreadLocal的静态内部类,每个Thread中都具备一个ThreadLocalMap,而ThreadLocalMap可以存储以ThreadLocal为 key ,Object 对象为 value 的键值对。

进入ThreadLocalMap这个类看一下: 

如下图,比如我们在同一个线程中声明了两个 ThreadLocal对象的话,会使用 Thread内部都是使用仅有那个ThreadLocalMap存放数据的,ThreadLocalMap的 key 就是 ThreadLocal对象,value 就是 ThreadLocal对象调用set方法设置的值。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/346037.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号