当前位置:首页 > 技术 >

threadlocal 使用场景(threadlocal详细介绍)

来源:原点资讯(www.yd166.com)时间:2023-04-15 23:45:51作者:YD166手机阅读>>

threadlocal 使用场景,threadlocal详细介绍(1)

两大使用场景-ThreadLocal的用途

典型场景1: 每个线程需要一个独享的对象(通常是工具类,典型需要使用的类有SimpleDateFormat和Random)

典型场景2: 每个线程内需要保存全局变量(例如在拦截器中获取用户信息),可以让不同方法直接使用,避免参数传递的麻烦。

典型场景1:每个线程需要一个独享的对象

每个Thread内有自己的实例副本,不共享;

举例:SimpleDateFormat。(当多个线程共用这样一个SimpleDateFormat,但是这个类是不安全的)

  • 2个线程分别用自己的SimpleDateFormat,这没问题;
  • 后来延伸出10个,那就有10个线程和10个SimpleDateFormat,这虽然写法不优雅,但勉强可以接受
  • 但是当需求变成了1000,那么必然要用线程池,消耗内存太多;
  • 但是每一个SimpleDateFormat我们都需要创建一遍,那么太耗费new对象了,改成static共用的,所有线程都共用一个simpleDateFormat对象,但这是线程不安全的,容易出现时间一致的情况,在调用的时候,可加锁来解决,但还是不优雅;
  • 用ThreadLocal来解决该问题,给每个线程分配一个simpledateformat,可这个threadlocal是安全的;

packagethreadlocal; importjava.text.SimpleDateFormat; importjava.util.Date; importjava.util.concurrent.ExecutorService; importjava.util.concurrent.Executors; /** *描述:利用ThreadLocal,给每个线程分配自己的dateFormat对象,保证了线程安全,高效利用内存 */ publicclassThreadLocalNormalUsage05{ publicstaticExecutorServicethreadPool=Executors.newFixedThreadPool(10); publicstaticvoidmain(String[]args)throwsInterruptedException{ for(inti=0;i<1000;i ){ intfinalI=i; threadPool.submit(newRunnable(){ @Override publicvoidrun(){ Stringdate=newThreadLocalNormalUsage05().date(finalI); System.out.println(date); } }); } threadPool.shutdown(); } publicStringdate(intseconds){ //参数的单位是毫秒,从1970.1.100:00:00GMT计时 Datedate=newDate(1000*seconds); //SimpleDateFormatdateFormat=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss"); SimpleDateFormatdateFormat=ThreadSafeFormatter.dateFormatThreadLocal2.get(); returndateFormat.format(date); } } classThreadSafeFormatter{ publicstaticThreadLocal<SimpleDateFormat>dateFormatThreadLocal=newThreadLocal<SimpleDateFormat>(){ @Override protectedSimpleDateFormatinitialValue(){ returnnewSimpleDateFormat("yyyy-MM-ddHH:mm:ss"); } }; publicstaticThreadLocal<SimpleDateFormat>dateFormatThreadLocal2=ThreadLocal .withInitial(()->newSimpleDateFormat("yyyy-MM-ddHH:mm:ss")); } 典型场景2:当前用户信息需要被线程内所有方法共享

  • 一个比较繁琐的解决方案是把user作为参数层层传递,从service-1()传到service-2(),以此类推,但是这样做会导致代码冗余且不易维护。
  • 进阶点就是userMap来保存,但是当多线程同时工作时,需要保证线程安全,需要用synchronized,或者concurrenthashmap,但无论用什么,都会对性能有所影响

每个线程内需要保存全局变量,可以让不同方法直接使用,避免参数传递的麻烦

  • 用ThreadLocal保存一些业务内存(用户权限信息,从用户系统获取到的用户名、userId等)
  • 这些信息在同一个线程内相同,但是不同的线程使用的业务内容是不相同的
  • 在线程生命周期内,都通过这个静态ThreadLocal实例的get()方法取得自己set过的那个对象,避免了将这个对象作为参数传递的麻烦

packagethreadlocal; /** *描述:演示ThreadLocal用法2:避免传递参数的麻烦 */ publicclassThreadLocalNormalUsage06{ publicstaticvoidmain(String[]args){ newService1().process(""); } } classService1{ publicvoidprocess(Stringname){ Useruser=newUser("超哥"); UserContextHolder.holder.set(user); newService2().process(); } } classService2{ publicvoidprocess(){ Useruser=UserContextHolder.holder.get(); ThreadSafeFormatter.dateFormatThreadLocal.get(); System.out.println("Service2拿到用户名:" user.name); newService3().process(); } } classService3{ publicvoidprocess(){ Useruser=UserContextHolder.holder.get(); System.out.println("Service3拿到用户名:" user.name); UserContextHolder.holder.remove(); } } classUserContextHolder{ publicstaticThreadLocal<User>holder=newThreadLocal<>(); } classUser{ Stringname; publicUser(Stringname){ this.name=name; } }

注意点:

  • 强调的是同一个请求内(同一个线程内)不同方法见的共享;
  • 不需重写initialValue()方法,但是必须手动调用set()方法
ThreadLocal方法使用总结场景一:initialValue

在ThreadLocal第一次get的时候把对象给初始化出来,对象的初始化时机可以由我们控制。

场景二:set

如果需要保存到ThreadLocal里面的对象的生成时机不由我们随意控制。例如拦截器生成的用户信息,用ThreadLocal.set直接放到ThreadLocal当中。

ThreadLocal原理

理清Thread,ThreadLocalMap以及ThreadLocal

threadlocal 使用场景,threadlocal详细介绍(2)

主要方法介绍

  • T initialValue(): 初始化
  • void set(T t): 为这个线程设置一新值
  • T get(): 得到这个线程对应的value。如果是首次调用get()。则会调用initialize来得到这个值
  • void remove(): 删除这个线程得到的值

ThreadLocalMap发生冲突之后,会用线性探测法。另外,欢迎关注公众号Java笔记虾,后台回复“后端面试”,送你一份面试题宝典!

ThreadLocal使用问题内存泄露什么是内存泄露

某个对象不再有用,但是占用的内存却不能被回收。

Value的泄露

  • 在ThreadLocalMap中的每个Entry都是一个对key的弱引用,同时,每个Entry都包含了一个对value的强引用。
  • 正常情况 ,当线程终止,保存在ThreadLocal里的value会被垃圾回收,因为没有任何强引用了。
  • 但是,如果线程不终止(比如线程池需要保持很久),那么key对应的value就不能被回收。Thread->ThreadLocalMap->Entry(key为Null)->Value
  • 因为value和Thread之间还存在这个强引用链路,所以导致value无法回收,就可能出现OOM;JDK已经考虑到这个问题,所以在set,remove,rehash方法中会扫描key为null,会把value也设置为null,这样value对象就可以被回收了。
  • 但是如果一个ThreadLocal不被使用,那么实际上set,remove,rehash方法也不会被调用,如果同时线程又不停止,那么调用链就一直存在,那么就导致了value的内存泄露。
如何避免内存泄露呢?
  • 调用remove方法,就会删除对应的Entry对象,可以避免内存泄露,所以使用完ThreadLocal之后,应该调用remove方法。
实际应用场景-在spring中的实例分析
  • DateTimeContextHolder:用到了ThreadLocal
  • RequestContextHolder:用到了ThreadLocal

栏目热文

threadlocal底层原理(ThreadLocal原理)

threadlocal底层原理(ThreadLocal原理)

引言这是JWT认证条件下的getCurrentLoginUser代码实现,请分析性能:在生产环境中,currentLog...

2023-04-15 23:51:07查看全文 >>

threadlocal用法示例(threadlocal使用场景)

threadlocal用法示例(threadlocal使用场景)

1.ThreadLocal的使用场景1.1 场景1每个线程需要一个独享对象(通常是工具类,典型需要使用的类有Simple...

2023-04-15 23:54:10查看全文 >>

threadlocal使用场景(threadlocal不能使用的场景)

threadlocal使用场景(threadlocal不能使用的场景)

推荐学习 前言相信很多同学都听过threadLocal,即使没用过也听过。但是要仔细一问ThreadLocal是个啥,...

2023-04-15 23:59:17查看全文 >>

threadlocal为什么是弱引用(threadlocal为什么是安全的)

threadlocal为什么是弱引用(threadlocal为什么是安全的)

前言大家好,我是捡田螺的小男孩。无论是工作还是面试,我们都会跟ThreadLocal打交道,今天就跟大家聊聊Thread...

2023-04-15 23:32:30查看全文 >>

threadlocal使用实例(threadlocal例子)

threadlocal使用实例(threadlocal例子)

如何统计时间说着说着,慧能写了一段程序说着说着一尘就随手写了一段程序经验丰富的慧能一眼就看出来这个程序的致命缺陷这个程序...

2023-04-15 23:18:30查看全文 >>

threadlocal实例(threadlocal使用范例)

threadlocal实例(threadlocal使用范例)

作者:@adamhandzybuluo.com/adamhand/note/1370920ThreadLocal是什么首...

2023-04-15 23:56:45查看全文 >>

threadlocal的底层原理(threadlocal实际中的作用)

threadlocal的底层原理(threadlocal实际中的作用)

1、ThreadLocal的底层原理图说明:Thread中有threadLocals成员变量,threadLocal会在...

2023-04-15 23:59:19查看全文 >>

threadlocal 存在意义(threadlocal存在哪里)

threadlocal 存在意义(threadlocal存在哪里)

功能迭代,在代码层面小编有1w种实现方法(吹牛的),一起来看看这次小编如何使用ThreadLocal优雅地完成本次迭代吧...

2023-04-15 23:15:22查看全文 >>

javathreadlocal原理(java thread 方法详解)

javathreadlocal原理(java thread 方法详解)

图解:该图基于Android中的ThreadLocal在Looper中的应用,其能够实现一个线程只有一个Looper的私...

2023-04-15 23:23:59查看全文 >>

thread local使用场景和原理

thread local使用场景和原理

是什么ThreadLocal从名字上看好像是一个Thread,其实并不是,它是Therad的局部变量的维护类。作用是让变...

2023-04-15 23:35:49查看全文 >>

文档排行