DNA图谱 / 问答 / 问答详情

合理使用线程池以及线程变量

2023-07-01 09:47:04
共1条回复
豆豆staR

背景

随着计算技术的不断发展,3纳米制程芯片已进入试产阶段,摩尔定律在现有工艺下逐渐面临巨大的物理瓶颈,通过多核处理器技术来提升服务器的性能成为提升算力的主要方向。

在服务器领域,基于java构建的后端服务器占据着领先地位,因此,掌握java并发编程技术,充分利用CPU的并发处理能力是一个开发人员必修的基本功,本文结合线程池源码和实践,简要介绍了线程池和线程变量的使用。

线程池概述

线程池是一种“池化”的线程使用模式,通过创建一定数量的线程,让这些线程处于就绪状态来提高系统响应速度,在线程使用完成后归还到线程池来达到重复利用的目标,从而降低系统资源的消耗。

总体来说,线程池有如下的优势:

线程池的使用

在java中,线程池的实现类是ThreadPoolExecutor,构造函数如下:

可以通过 new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory,handler)来创建一个线程池。

在构造函数中,corePoolSize为线程池核心线程数。默认情况下,核心线程会一直存活,但是当将allowCoreThreadTimeout设置为true时,核心线程超时也会回收。

在构造函数中,maximumPoolSize为线程池所能容纳的最大线程数。

在构造函数中,keepAliveTime表示线程闲置超时时长。如果线程闲置时间超过该时长,非核心线程就会被回收。如果将allowCoreThreadTimeout设置为true时,核心线程也会超时回收。

在构造函数中,timeUnit表示线程闲置超时时长的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。

在构造函数中,blockingQueue表示任务队列,线程池任务队列的常用实现类有:

在构造函数中,threadFactory表示线程工厂。用于指定为线程池创建新线程的方式,threadFactory可以设置线程名称、线程组、优先级等参数。如通过Google工具包可以设置线程池里的线程名:

在构造函数中,rejectedExecutionHandler表示拒绝策略。当达到最大线程数且队列任务已满时需要执行的拒绝策略,常见的拒绝策略如下:

ThreadPoolExecutor线程池有如下几种状态:

线程池提交一个任务时任务调度的主要步骤如下:

核心代码如下:

Tomcat 的整体架构包含连接器和容器两大部分,其中连接器负责与外部通信,容器负责内部逻辑处理。在连接器中:

Tomcat为了实现请求的快速响应,使用线程池来提高请求的处理能力。下面我们以HTTP非阻塞I/O为例对Tomcat线程池进行简要的分析。

在Tomcat中,通过AbstractEndpoint类提供底层的网络I/O的处理,若用户没有配置自定义公共线程池,则AbstractEndpoint通过createExecutor方法来创建Tomcat默认线程池。

核心部分代码如下:

其中,TaskQueue、ThreadPoolExecutor分别为Tomcat自定义任务队列、线程池实现。

Tomcat自定义线程池继承于java.util.concurrent.ThreadPoolExecutor,并新增了一些成员变量来更高效地统计已经提交但尚未完成的任务数量(submittedCount),包括已经在队列中的任务和已经交给工作线程但还未开始执行的任务。

Tomcat在自定义线程池ThreadPoolExecutor中重写了execute()方法,并实现对提交执行的任务进行submittedCount加一。Tomcat在自定义ThreadPoolExecutor中,当线程池抛出RejectedExecutionException异常后,会调用force()方法再次向TaskQueue中进行添加任务的尝试。如果添加失败,则submittedCount减一后,再抛出RejectedExecutionException。

在Tomcat中重新定义了一个阻塞队列TaskQueue,它继承于LinkedBlockingQueue。在Tomcat中,核心线程数默认值为10,最大线程数默认为200, 为了避免线程到达核心线程数后后续任务放入队列等待,Tomcat通过自定义任务队列TaskQueue重写offer方法实现了核心线程池数达到配置数后线程的创建。

具体地,从线程池任务调度机制实现可知,当offer方法返回false时,线程池将尝试创建新新线程,从而实现任务的快速响应。TaskQueue核心实现代码如下:

Tomcat中通过自定义任务线程TaskThread实现对每个线程创建时间的记录;使用静态内部类WrappingRunnable对Runnable进行包装,用于对StopPooledThreadException异常类型的处理。

Executors常用方法有以下几个:

Executors类看起来功能比较强大、用起来还比较方便,但存在如下弊端

使用线程时,可以直接调用 ThreadPoolExecutor 的构造函数来创建线程池,并根据业务实际场景来设置corePoolSize、blockingQueue、RejectedExecuteHandler等参数。

使用局部线程池时,若任务执行完后没有执行shutdown()方法或有其他不当引用,极易造成系统资源耗尽。

在工程实践中,通常使用下述公式来计算核心线程数:

nThreads=(w+c)/c*n*u=(w/c+1)*n*u

其中,w为等待时间,c为计算时间,n为CPU核心数(通常可通过 Runtime.getRuntime().availableProcessors()方法获取),u为CPU目标利用率(取值区间为[0, 1]);在最大化CPU利用率的情况下,当处理的任务为计算密集型任务时,即等待时间w为0,此时核心线程数等于CPU核心数。

上述计算公式是理想情况下的建议核心线程数,而不同系统/应用在运行不同的任务时可能会有一定的差异,因此最佳线程数参数还需要根据任务的实际运行情况和压测表现进行微调。

为了更好地发现、分析和解决问题,建议在使用多线程时增加对异常的处理,异常处理通常有下述方案:

为了实现优雅停机的目标,我们应当先调用shutdown方法,调用这个方法也就意味着,这个线程池不会再接收任何新的任务,但是已经提交的任务还会继续执行。之后我们还应当调用awaitTermination方法,这个方法可以设定线程池在关闭之前的最大超时时间,如果在超时时间结束之前线程池能够正常关闭则会返回true,否则,超时会返回false。通常我们需要根据业务场景预估一个合理的超时时间,然后调用该方法。

如果awaitTermination方法返回false,但又希望尽可能在线程池关闭之后再做其他资源回收工作,可以考虑再调用一下shutdownNow方法,此时队列中所有尚未被处理的任务都会被丢弃,同时会设置线程池中每个线程的中断标志位。shutdownNow并不保证一定可以让正在运行的线程停止工作,除非提交给线程的任务能够正确响应中断。

ThreadLocal线程变量概述

ThreadLocal类提供了线程本地变量(thread-local variables),这些变量不同于普通的变量,访问线程本地变量的每个线程(通过其get或set方法)都有其自己的独立初始化的变量副本,因此ThreadLocal没有多线程竞争的问题,不需要单独进行加锁。

ThreadLocal的原理与实践

对于ThreadLocal而言,常用的方法有get/set/initialValue 3个方法。

众所周知,在java中SimpleDateFormat有线程安全问题,为了安全地使用SimpleDateFormat,除了1)创建SimpleDateFormat局部变量;和2)加同步锁 两种方案外,我们还可以使用3)ThreadLocal的方案:

Thread 内部维护了一个 ThreadLocal.ThreadLocalMap 实例(threadLocals),ThreadLocal 的操作都是围绕着 threadLocals 来操作的。

从JDK源码可见,ThreadLocalMap中的Entry是弱引用类型的,这就意味着如果这个ThreadLocal只被这个Entry引用,而没有被其他对象强引用时,就会在下一次GC的时候回收掉。

EagleEye(鹰眼)作为全链路监控系统在集团内部被广泛使用,traceId、rpcId、压测标等信息存储在EagleEye的ThreadLocal变量中,并在HSF/Dubbo服务调用间进行传递。EagleEye通过Filter将数据初始化到ThreadLocal中,部分相关代码如下:

在EagleEyeFilter中,通过EagleEyeRequestTracer.startTrace方法进行初始化,在前置入参转换后,通过startTrace重载方法将鹰眼上下文参数存入ThreadLocal中,相关代码如下:

EagleEyeFilter在finally代码块中,通过EagleEyeRequestTracer.endTrace方法结束调用链,通过clear方法将ThreadLocal中的数据进行清理,相关代码实现如下:

在某权益领取原有链路中,通过app打开一级页面后才能发起权益领取请求,请求经过淘系无线网关(Mtop)后到达服务端,服务端通过mtop sdk获取当前会话信息。

在XX项目中,对权益领取链路进行了升级改造,在一级页面请求时,通过服务端同时发起权益领取请求。具体地,服务端在处理一级页面请求时,同时通过调用hsf/dubbo接口来进行权益领取,因此在发起rpc调用时需要携带用户当前会话信息,在服务提供端将会话信息进行提取并注入到mtop上下文,从而才能通过mtop sdk获取到会话id等信息。某开发同学在实现时,因ThreadLocal使用不当造成下述问题:

【问题1:权益领取失败分析】

在权益领取服务中,该应用构建了一套高效和线程安全的依赖注入框架,基于该框架的业务逻辑模块通常抽象为xxxModule形式,Module间为网状依赖关系,框架会按依赖关系自动调用init方法(其中,被依赖的module 的init方法先执行)。

在应用中,权益领取接口的主入口为CommonXXApplyModule类,CommonXXApplyModule依赖XXSessionModule。当请求来临时,会按依赖关系依次调用init方法,因此XXSessionModule的init方法会优先执行;而开发同学在CommonXXApplyModule类中的init方法中通过调用recoverMtopContext()方法来期望恢复mtop上下文,因recoverMtopContext()方法的调用时机过晚,从而导致XXSessionModule模块获取不到正确的会话id等信息而导致权益领取失败。

【问题2:脏数据分析】

权益领取服务在处理请求时,若当前线程曾经处理过权益领取请求,因ThreadLocal变量值未被清理,此时XXSessionModule通过mtop SDK获取会话信息时得到的是前一次请求的会话信息,从而造成脏数据。

【解决方案】

在依赖注入框架入口处AbstractGate#visit(或在XXSessionModule中)通过recoverMtopContext方法注入mtop上下文信息,并在入口方法的finally代码块清理当前请求的threadlocal变量值。

若使用强引用类型,则threadlocal的引用链为:Thread -> ThreadLocal.ThreadLocalMap -> Entry[] -> Entry -> key(threadLocal对象)和value;在这种场景下,只要这个线程还在运行(如线程池场景),若不调用remove方法,则该对象及关联的所有强引用对象都不会被垃圾回收器回收。

若使用static关键字进行修饰,则一个线程仅对应一个线程变量;否则,threadlocal语义变为perThread-perInstance,容易引发内存泄漏,如下述示例:

在上述main方法第22行debug,可见线程的threadLocals变量中有3个threadlocal实例。在工程实践中,使用threadlocal时通常期望一个线程只有一个threadlocal实例,因此,若不使用static修饰,期望的语义发生了变化,同时易引起内存泄漏。

如果不执行清理操作,则可能会出现:

建议使用try...finally 进行清理。

我们在使用ThreadLocal时,通常期望的语义是perThread,若不使用static进行修饰,则语义变为perThread-perInstance;在线程池场景下,若不用static进行修饰,创建的线程相关实例可能会达到 M * N个(其中M为线程数,N为对应类的实例数),易造成内存泄漏(https://errorprone.info/bugpattern/ThreadLocalUsage)。

在应用中,谨慎使用ThreadLocal.withInitial(Supplier<? extends S> supplier)这个工厂方法创建ThreadLocal对象,一旦不同线程的ThreadLocal使用了同一个Supplier对象,那么隔离也就无从谈起了,如:

总结

在java工程实践中,线程池和线程变量被广泛使用,因线程池和线程变量的不当使用经常造成安全生产事故,因此,正确使用线程池和线程变量是每一位开发人员必须修炼的基本功。本文从线程池和线程变量的使用出发,简要介绍了线程池和线程变量的原理和使用实践,各开发人员可结合最佳实践和实际应用场景,正确地使用线程和线程变量,构建出稳定、高效的java应用服务。

相关推荐

并发编程-Threadlocal

上一篇 <<< Fork/Join框架 下一篇 >>> Disruptor框架 Threadlocal: 各个线程独有的局部变量,相互之间不受影响。 它主要有四个方法initialValue()、get()、set()和remove(),底层采用了map集合形式进行存放,key为当前线程ID。 不管是用强引用还是弱引用都是会发生内存泄漏的问题。弱引用中不会发生ThreadLocal内存泄漏的问题。 但是最终根本的原因Threadlocal内存泄漏的问题,产生于ThreadLocalMap与我们当前线程的生命周期一样,如果没有手动的删除的情况下,就有可能会发生内存泄漏的问题。 相关文章链接: <<< 多线程基础 <<< 线程安全与解决方案 <<< 锁的深入化 <<< 锁的优化 <<< Java内存模型(JMM) <<< Volatile解决JMM的可见性问题 <<< Volatile的伪共享和重排序 <<< CAS无锁模式及ABA问题 <<< Synchronized锁 <<< Lock锁 <<< AQS同步器 <<< Condition <<< CountDownLatch同步计数器 <<< Semaphore信号量 <<< CyclicBarrier屏障 <<< 线程池 <<< 并发队列 <<< Callable与Future模式 <<< Fork/Join框架 <<< Disruptor框架 <<< 如何优化多线程总结
2023-07-01 08:25:441

java的ThreadLocal是否能被其他线程访问到

threadlocal是针对 per-thread的,不会被其他线程并发访问。 threadlocal变量和单个线程关联,简单来讲,类似于(具体可参考jdk源码): Java代码 public class FakeThreadLocal<T> { private final Map<Thread, T> values = new HashMap<Thread, T>(); public T get() { return values.get(Thread.currentThread()); } public void set(T t) { values.put(Thread.currentThread(), t); } } 应用场景1,创建一个 SimpleDateFormat实例的开销比较昂贵,解析字符串时间时频繁创建生命周期短暂的实例导致性能低下。即使将 SimpleDateFormat定义为静态类变量,貌似能解决这个问题,但是SimpleDateFormat是非线程安全的,同样存在问题,如果用 ‘synchronized"线程同步同样面临问题,同步导致性能下降(线程之间序列化的获取SimpleDateFormat实例)。 使用Threadlocal解决了此问题,对于每个线程SimpleDateFormat不存在影响他们之间协作的状态,为每个线程创建一个SimpleDateFormat变量的拷贝或者叫做副本。 Java代码 public class Test { // SimpleDateFormat is not thread-safe, so give one to each thread private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){ @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyyMMdd HHmm"); } }; public String formatIt(Date date) { return formatter.get().format(date); } } 应用场景2,在servlet/filter中保存初始状态,以便解决并发问题。 应用场景3,在一些框架中保存和当前线程有关的上下文。
2023-07-01 08:25:502

ThreadLocal的使用方法是什么?

早在Java 1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序时提供了一种新的选择。使用这个工具类可以很简洁地编写出优美的多线程程序,虽然ThreadLocal非常有用,但是似乎现在了解它、使用它的朋友还不多。    ThreadLocal是什么    ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。线程局部变量并不是Java的新发明,在其它的一些语言编译器实现(如IBM XL FORTRAN)中,它在语言的层次提供了直接的支持。因为Java中没有提供在语言层次的直接支持,而是提供了一个ThreadLocal的类来提供支持,所以,在Java中编写线程局部变量的代码相对比较笨拙,这也许是线程局部变量没有在Java中得到很好的普及的一个原因吧。    ThreadLocal的设计    首先看看ThreadLocal的接口:     Object get() ; // 返回当前线程的线程局部变量副本 protected Object initialValue(); // 返回该线程局部变量的当前线程的初始值     void set(Object value); // 设置当前线程的线程局部变量副本的值    ThreadLocal有3个方法,其中值得注意的是initialValue(),该方法是一个protected的方法,显然是为了子类重写而特意实现的。该方法返回当前线程在该线程局部变量的初始值,这个方法是一个延迟调用方法,在一个线程第1次调用get()或者set(Object)时才执行,并且仅执行1次。ThreadLocal中的确实实现直接返回一个null:   protected Object initialValue() { return null; }   ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现:   public class ThreadLocal   {    private Map values = Collections.synchronizedMap(new HashMap());    public Object get()    {    Thread curThread = Thread.currentThread();    Object o = values.get(curThread);    if (o == null && !values.containsKey(curThread))    {     o = initialValue();     values.put(curThread, o);    }    return o;    }    public void set(Object newValue)    {    values.put(Thread.currentThread(), newValue);    }    public Object initialValue()    {    return null;    }   }   当然,这并不是一个工业强度的实现,但JDK中的ThreadLocal的实现总体思路也类似于此。    ThreadLocal的使用    如果希望线程局部变量初始化其它值,那么需要自己实现ThreadLocal的子类并重写该方法,通常使用一个内部匿名类对ThreadLocal进行子类化,比如下面的例子,SerialNum类为每一个类分配一个序号:   public class SerialNum   {    // The next serial number to be assigned    private static int nextSerialNum = 0;    private static ThreadLocal serialNum = new ThreadLocal()    {    protected synchronized Object initialValue()    {     return new Integer(nextSerialNum++);    }    };    public static int get()    {    return ((Integer) (serialNum.get())).intValue();    }   }   SerialNum类的使用将非常地简单,因为get()方法是static的,所以在需要获取当前线程的序号时,简单地调用:   int serial = SerialNum.get();   即可。    在线程是活动的并且ThreadLocal对象是可访问的时,该线程就持有一个到该线程局部变量副本的隐含引用,当该线程运行结束后,该线程拥有的所以线程局部变量的副本都将失效,并等待垃圾收集器收集。    ThreadLocal与其它同步机制的比较    ThreadLocal和其它同步机制相比有什么优势呢?ThreadLocal和其它所有的同步机制都是为了解决多线程中的对同一变量的访问冲突,在普通的同步机制中,是通过对象加锁来实现多个线程对同一变量的安全访问的。这时该变量是多个线程共享的,使用这种同步机制需要很细致地分析在什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放该对象的锁等等很多。所有这些都是因为多个线程共享了资源造成的。ThreadLocal就从另一个角度来解决多线程的并发访问,ThreadLocal会为每一个线程维护一个和该线程绑定的变量的副本,从而隔离了多个线程的数据,每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的整个变量封装进ThreadLocal,或者把该对象的特定于线程的状态封装进ThreadLocal。    由于ThreadLocal中可以持有任何类型的对象,所以使用ThreadLocal get当前线程的值是需要进行强制类型转换。但随着新的Java版本(1.5)将模版的引入,新的支持模版参数的ThreadLocal类将从中受益。也可以减少强制类型转换,并将一些错误检查提前到了编译期,将一定程度地简化ThreadLocal的使用。    总结    当然ThreadLocal并不能替代同步机制,两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;而ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不需要对多个线程进行同步了。所以,如果你需要进行多个线程之间进行通信,则使用同步机制;如果需要隔离多个线程之间的共享冲突,可以使用ThreadLocal,这将极大地简化你的程序,使程序更加易读、简洁。
2023-07-01 08:25:571

ThreadLocal共享线程局部变量和线程同步机制的区别

  ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。  对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。  ThreadLocal 并不能替代同步机制,两者面向的问题领域不同。  1:同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;  2:而threadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享变量,这样当然不需要对多个线程进行同步了。  import java.util.Random;public class ThreadSocpeShareData { static ThreadLocal<Integer> t = new ThreadLocal<Integer>(); public static void main(String[] args) { for(int i=0;i<3;i++){ new Thread(new Runnable() { @Override public void run() { int data = new Random().nextInt(); System.out.println(Thread.currentThread().getName() +" has put "+ data); t.set(data); MyThreadScopeData.getInstance().setName("name" + data); MyThreadScopeData.getInstance().setAge("age"+data); new A().get(); new B().get(); } }).start(); } } static class A{ public void get(){ int data = t.get(); MyThreadScopeData myData = MyThreadScopeData.getInstance(); System.out.println("A " + Thread.currentThread().getName() +" "+ data + myData.getAge() + myData.getName() /*ms.getName()*/); } } static class B{ public void get(){ int data = t.get(); System.out.println("B " + Thread.currentThread().getName()+ " "+ data); } }}class MyThreadScopeData{ private MyThreadScopeData(){} private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>(); public static MyThreadScopeData getInstance(){ MyThreadScopeData instance = map.get(); if(instance == null){ instance = new MyThreadScopeData(); map.set(instance); } return instance; }private String name; private String age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; }}  事实上,我们向ThreadLocal中set的变量不是由ThreadLocal来存储的,而是Thread线程对象自身保存。当用户调用ThreadLocal对象的set(Object o)时,该方法则通过Thread.currentThread()获取当前线程,将变量存入Thread中的一个Map内,而Map的Key就是当前的ThreadLocal实例。请看源码,这是最主要的两个函数,能看出ThreadLocal与Thread的调用关系:  public void set(T value) {    Thread t = Thread.currentThread();      ThreadLocalMap map = getMap(t);  if (map != null)        map.set(this, value);  else      createMap(t, value);  }  ThreadLocalMap getMap(Thread t) {  return t.threadLocals;  }   public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } ThreadLocalMap getMap(Thread t) { return t.threadLocals; }  具体可以看看里面的源码,不过有一点是可以证实的,就是Threadlocal中 创建的线程副本,可以不调用remove来做清理工作,因为jvm在发现线程的调佣不再使用时,会进行自动的垃圾回收操作,我以前写程序在使用Threadlocal时却是经常的进行使用完成之后的清理工作。(防止占用内存,如果创建的副本数量不是太多的话,可以让虚拟机自动来清除)
2023-07-01 08:26:041

什么是ThreadLocal?解决了什么问题?

首先加深印象: 1.ThreadLocal解决的是每个线程需要有自己独立的实例,且这个实例的修改不会影响到其他线程。 这个ThreadLocal的用法一般都是创建各自线程自己运行过程中单独创建的对象的,不适合的相同实例共享的。 https://ask.csdn.net/questions/764337 ThreadLocal存入的对象就不该是同一个。这玩意不保证线程安全。 所谓的副本是指,A线程存入的值,对B线程并不感知,B只能拿到自己存的值,并不能拿到A存入的值。 因为一般情况下ThreadLocal 都是定义为static类型的,如果没有ThreadLocal,那么B线程就可以获取A线程所存入的值。 理解了使用场景,很多地方就能想通了 2.ThreadLocal造成内存泄漏的问题我认为有两点: ThreadLocal原理总结 1.每个Thread维护着一个ThreadLocalMap的引用 2.ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储 3.调用ThreadLocal的set()方法时,实际上就是往ThreadLocalMap设置值,key是ThreadLocal对象,值是传递进来的对象 4.调用ThreadLocal的get()方法时,实际上就是往ThreadLocalMap获取值,key是ThreadLocal对象 5.ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。 这里放出一个程序可以思考一下会输出什么? 答案是 可以debug来看一下他的流程 第一次set,由于Thread中的map还没有被初始化,所以先创建map 因为第一个ThreadLocal已经初始化了map,所以map!=null为true, 到此为止,上面的程序也好理解了,
2023-07-01 08:26:111

什么是ThreadLocal?

  ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量。  ThreadLocal是Thread的局部变量。所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。  ThreadLocal的接口方法:  ThreadLocal类接口很简单,只有4个方法,先来了解一下:  void set(Object value)  public void remove()  将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 1.5 新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。  protected Object initialValue()  返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。  值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。  ThreadLocal为每一个线程维护变量的副本的思路:  在ThreadLocal类中定义了一个ThreadLocalMap,每一个Thread中都有一个该类型的变量——threadLocals——用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。
2023-07-01 08:26:192

Java:ThreadLocal究竟有什么用呢?费解

从如下8点来讲解一下:1.ThreadLocal用来解决多线程程序的并发问题2.ThreadLocal并不是一个Thread,而是Thread的局部变量,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本.3.从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。4.线程局部变量并不是Java的新发明,Java没有提供在语言级支持(语法上),而是变相地通过ThreadLocal的类提供支持.5.ThreadLocal类中的方法:(JDK5版本之后支持泛型) void set(T value) 将此线程局部变量的当前线程副本中的值设置为指定值 void remove() 移除此线程局部变量当前线程的值 protected T initialValue() 返回此线程局部变量的当前线程的“初始值” T get() 返回此线程局部变量的当前线程副本中的值6.ThreadLocal的原理: ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本7.自己模拟ThreadLocal: public class SimpleThreadLocal{ private Map valueMap=Collections.synchronizedMap(new HashMap()); public void set(Object newValue){ valueMap.put(Thread.currentThread(),newValue);//键为线程对象,值为本线程的变量副本 } public Object get(){ Thread currentThread=Thread.currentThread(); Object o=valueMap.get(currentThread);//返回本线程对应的变量 if(o==null&&!valueMap.containsKey(currentThread)){ //如果在Map中不存在,放到Map中保存起来 o=initialValue(); valueMap.put(currentThread,o); } return o; } public void remove(){ valueMap.remove(Thread.currentThread()); } public void initialValue(){ return null; } }8.使用ThreadLocal的具体例子: public class SequenceNumber{ //通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值 private static ThreadLocal<Integer> seNum=new ThreadLocal<Integer>(){ protected Integer initialValue(){ return 0; } } public Integer getNextNum(){ seNum.set(seNum.get()+1); return seNum.get(); } public static void main(String[] args){ SequenceNumber sn=new SequenceNumber(); //3个线程共享sn,各自产生序列号 TestClient t1 = new TestClient(sn); TestClient t2 = new TestClient(sn); TestClient t3 = new TestClient(sn); t1.start(); t2.start(); t3.start(); } private static class TestClient extends Thread{ private SequenceNumber sn; public TestClient(SequenceNumber sn){ this.sn=sn; } public void run(){ //每个线程打印3个序列号 for(int i=0;i<3;i++){ System.out.println("thread["+Thread.currentThread().getName()+",sn["+sn.getNextNum()+"]"); } } } }
2023-07-01 08:26:273

ThreadLocal本质及脏数据、内存泄漏问题

ThreadLocal作为WeakReference的referent,只要ThreadLocal对象引用被置为null,Entry的key(referent)就会在下一次YGC时被回收。在使用ThreadLocal的get()和set()时,会将失效的Entry(key == null)的value置为null,使value能够被GC回收,避免内存泄漏。 ThreadLocal对象常作为私有静态变量使用,其生命周期不会随着线程结束而结束。 ThreadLocal本质属性:线程间共享。 调用该init的有8个Thread构造器: 这8个构造器的inheritThreadLocals为true。 只有下面的构造器inheritThreadLocals为false: 在init中: InheritableThreadLocal用来给子线程从父线程继承thread-local值。 InheritableThreadLocal自动与线程的inheritableThreadLocals域关联起来。 childValue()是个protected方法,其用在子线程创建时,用来从父线程copy thread-local值时,可以设置初始值。 使用ThreadLocal和InheritableThreadLocal透传上下文时,需要注意线程间切换、异常传输时的处理,避免在传输过程中因处理不当而导致的上下文丢失。 ThreadLocal对象本身由多线程共享。 而T类型的值各线程各不相同。 例如SimpleDateFormat是线程不安全的,可以放到ThreadLocal<DateFormat>中,然每个线程单独有一份SimpleDateFormat对象。 主要问题是会产生脏数据和内存泄漏。通常是在线程池的线程中使用ThreadLocal引发的,因为线程池有线程复用和内存常驻两个特点。 run()中没有显式地调用remove()清理与线程相关的ThreadLocal信息,下一个线程也没有set()设置初始值,则会产生脏数据。 ThreadLocal一般是static对象。因此寄希望于ThreadLocal对象失去引用后,触发弱引用回收机制来回收Value是不现实的。 为了防止内存泄漏,每次用完ThreadLocal,必须要及时调用remove()方法清理。
2023-07-01 08:26:341

什么是threadlocal变量

ThreadLocal变量就是和线程绑定的变量.实际上是一个Map,,,key是对应的线程,值则是该变量. 调用ThreadLocal的get方法时则会到Map中查询当前线程是否已拥有该变量,如果没有则新建一个并保存到Map中.有的话直接返回与该线程绑定的变量. 说白了就是
2023-07-01 08:26:422

ThreadLocal是如何实现保存线程私有对象的

最早知道ThreadLocal是在Looper的源码里,用一个ThreadLocal保存了当前的looper对象。 当时就查了下ThreadLocal,发现是保存线程私有对象的容器,以为就是一个类似hashmap,用线程做key,存value。最近看了下并不是如此,实际上是以ThreadLocal自身为key来存储对象的。于是来学习下ThreadLocal源码是如何做到保存线程私有对象的。 ThreadLocal、ThreadLocalMap、Thread之间的关系和我们下意识中想的不太一样,不过一步步看下去之后就能明白为啥ThreadLocal能保存线程私有对象了。 这个是Thread源码,每一个Thread都有一个ThreadLocalMap对象,而ThreadLocalMap是ThreadLocal的内部类,是实际存储对象的容器。 基本关系知道了,最后再来看看ThreadLocal的方法: 那么现在基本完全清楚ThreadLocal与Thread还有ThreadLocalMap之间的关系了。 每个Thread都有一个成员变量ThreadLocalMap。这个ThreadLocalMap对象不是public,所以外部调用不了,可以看做是线程私有对象。 ThreadLocalMap存了一个table,table里保存了一些entry,每个entry对应一个key和value,而这个key就是ThreadLocal对象。 因此一个ThreadLocal只能存一个value,但是可以通过new多个ThreadLocal来保存多个线程私有对象。 在上面的源码中我们看到Entry里持有的ThreadLocal对象是弱引用持有,因此ThreadLocal不会因为线程持有而泄露,比如我们Android的主线程,正常使用过程中是不会挂掉的。 但是Enrty的value的是强引用的,因此ThreadLocal中的value还是会因为线程持有而无法回收。如果继续看源码的话,会发现在ThreadLocalMap的resize和expungeStaleEntry方法里会检查key为空的value值为空帮助GC。 因此为了避免value内存泄露,我们需要在ThreadLocal不需要的时候主动remove掉。 ThreadLocal通过自身的threadLocalHashCode来碰撞得到自己在ThreadLocalMap的table里的索引i。因此这个threadLocalHashCode就十分重要了。 这里需要保证threadLocalHashCode是唯一的,否则两个线程同时创建ThreadLocal得到相同的hashcode,那就破坏了ThreadLocalMap的线程私有特性了。 这里生成threadLocalHashCode是通过一个静态对象nextHashCode不断增加来获得的。那怎么保证多个进程并发实例化ThreadLocal对象,不会生成相同的hashcode呢? 答案是AtomicInteger,通过这个类来保证变量自增操作在多线程操作时候的原子性。 我们知道Synchronized也可以保证原子性,但相比于它,AtomicInteger类是通过非阻塞方法来实现原子性的,需要的性能开销更小。 这种非阻塞实现原子性的方法和处理器的CAS指令有关,感兴趣的小伙伴自行研究吧~
2023-07-01 08:26:491

Java:ThreadLocal究竟有什么用呢?费解

1.ThreadLocal用来解决多线程程序的并发问题2.ThreadLocal并不是一个Thread,而是Thread的局部变量,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本.3.从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。4.线程局部变量并不是Java的新发明,Java没有提供在语言级支持(语法上),而是变相地通过ThreadLocal的类提供支持.5.ThreadLocal类中的方法:(JDK5版本之后支持泛型)void set(T value)将此线程局部变量的当前线程副本中的值设置为指定值void remove()移除此线程局部变量当前线程的值protected T initialValue()返回此线程局部变量的当前线程的“初始值”T get()返回此线程局部变量的当前线程副本中的值6.ThreadLocal的原理:ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本7.自己模拟ThreadLocal:1.ThreadLocal用来解决多线程程序的并发问题2.ThreadLocal并不是一个Thread,而是Thread的局部变量,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本.3.从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。4.线程局部变量并不是Java的新发明,Java没有提供在语言级支持(语法上),而是变相地通过ThreadLocal的类提供支持.5.ThreadLocal类中的方法:(JDK5版本之后支持泛型)void set(T value)将此线程局部变量的当前线程副本中的值设置为指定值void remove()移除此线程局部变量当前线程的值protected T initialValue()返回此线程局部变量的当前线程的“初始值”T get()返回此线程局部变量的当前线程副本中的值6.ThreadLocal的原理:ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本7.自己模拟ThreadLocal:
2023-07-01 08:27:092

ThreadLocal 方式存储用户信息

因为每次业务层 或者 其他非 Controller 层,需要用到用户信息的时候,就需要把当前用户信息先查询出来在传递,或者是直接把当前的 session 直接往下层传递。这样很繁琐。 可以使用 线程封闭 性来优化这个问题。 1.每次请求的时候,可能不是同一个线程去执行,会到导致用户数据获取不到。 2.线程要释放绑定的用户数据,不然会出现内存泄露的问题。 先创建 ThreadLocal 操作类 用户session操作类,针对 ThreadLocal 操作二次封装 定义一个 Filter 解决,每次请求不是同一个线程,造成用户信息获取不到问题 定义一个 Request 上下文监听器,用于释放线程绑定的用户信息,避免内存泄露 这里也可以选择 Spring Mvc提供的拦截器。但是我这里选择用 Servlet 的原生接口实现 看看结果: Servlet监听器用于监听一些重要事件的发生,监听器对象可以在事情发生前、发生后可以做一些必要的处理。下面将介绍几种常用的监听器,以及它们都适合运用于那些环境。 分类及介绍: 1. ServletContextListener :用于监听WEB 应用启动和销毁的事件,监听器类需要实现javax.servlet.ServletContextListener 接口。 2. ServletContextAttributeListener :用于监听WEB应用属性改变的事件,包括:增加属性、删除属性、修改属性,监听器类需要实现javax.servlet.ServletContextAttributeListener接口。 3. ServletRequestListener : 用于监听请求的创建于销毁, 需要实现 javax.servlet.ServletRequestListeneru200b 接口 4. ServletRequestAttributeListener :请求属性事件监听器。用于监听请求中的属性改变的事件。 5. HttpSessionListener :用于监听Session对象的创建和销毁,监听器类需要实现javax.servlet.http.HttpSessionListener接口或者javax.servlet.http.HttpSessionActivationListener接口,或者两个都实现。 6. HttpSessionActivationListener :用于监听Session对象的钝化/活化事件,监听器类需要实现javax.servlet.http.HttpSessionListener接口或者javax.servlet.http.HttpSessionActivationListener接口,或者两个都实现。 7. HttpSessionAttributeListener :用于监听Session对象属性的改变事件,监听器类需要实现 8. HttpSessionBindingListener :会话值绑定事件监听器。这是唯一不需要在web.xml中设定的Listener。 9. Filter :用于在客户端的请求访问后端资源之前,拦截这些请求 或者 在服务器的响应发送回客户端之前,处理这些响应
2023-07-01 08:27:161

ThreadLocal父子线程数据传递解决方案

在阅读前,您需要对ThreadLocal用法有一定了解。如果不了解,请参考ThreadLocal用法相关文章。 这个需求或许也很常见,最开始,我们可能会想到InheritableThreadLocal,这是由Jdk为我们提供,他是ThreadLocal的子类,使用方法和ThreadLocal完全相同。 但是这里有几个坑: 1、手动通过线程池创建线程可能会造成get值为null。 2、项目中我们往往会使用线程池,如果主线程使用的是缓存线程池(比如SpringMvc),线程会复用,当线程执行完毕后本次操作后,再次执行新的任务时候,ThreadLocal内部数据并没有被清除。 3、ThreadLocal父子线程之间数据拷贝默认是浅拷贝,这也就意味如果我们多个线程可能会引用同一个内存地址,造成多个线程访问一个对象,轻者会造成线程不安全,重者甚至会ThreadLocal数据被修改成非预期结果。 这些坑本人亲身体验过,血的教训总结出来,那么如何很好的解决这些问题呢?这边给出一个终级解决方案,使用阿里Transmittable ThreadLocal,可以很好的解决上面这些问题。 具体使用方法可以参见文档: https://github.com/alibaba/transmittable-thread-local 如果使用后发现在缓存线程池下仍然会出现ThreadLocal没有被清理干净的问题,可可尝试重写TransmittableThreadLocal afterExecute方法,在此方法中清除线程数据。 那么通过阿里的TransmittableThreadLocal 如何解决浅拷贝问题呢?这里它提供了一个copy方法,TransmittableThreadLocal 在进行对象拷贝的时候会调用copy方法,我们只需要重写这个方法,让他变量深复制即可。 最后,建议大家不要轻意使用ThreadLocal,能不用尽量不要去使用,一旦在缓存线程池下如果数据没有清理干净会很麻烦,不方便问题排查,而且ThreadLocal有一定情况下会有造成内存泄露的风险,一定要慎用!慎用!慎用!但并不意味着禁用!
2023-07-01 08:27:221

ThreadLocal如何保证一个线程只能有一个Looper?

我们都知道在调用Looper.prepare的时候会创建一个Looper,那么是如何保证一个线程只有一个Looper的? 首先要知道Looper中有一个sThreadLocal变量,ThreadLocal用于存储上下文信息 并且用final static 修饰,所以它是唯一的内容不可变的 了解sThreadLocal是干啥用的后,再来看看prepare 先调用sThreadLocal.get()方法 而ThreadLocalMap 是一个HashMap,那么取到一个HashMap后判断是否为null 如果不存在key为sThreadLocal的节点,得到value = null,并把这个value作为sThreadLocal的值即<sThreadLocal,null>;如果map为null,则创建一个HashMap并把<sThreadLocal,null>节点加入 这样get方法就要么取到一个Looper,要么就是null,如果为Looper则抛异常,如果为null,则调用sThreadLocal.set() 其实都是把Looper作为sThreadLocal的value值 回到开头说的,怎么保证一个线程只有一个Looper? 因为sThreadLocal是线程的上下文,并且唯一,而线程中存有<sThreadLocal,Looper>key-value键值对,所以一个sThreadLocal对应一个Looper,并且再次修改Looper是,会抛异常,因为Looper已经存在。 所以一个线程只有一个Looper。 如果对HashMap还不了解的同学,这篇文章可能对你有一定帮助 HashMap原理
2023-07-01 08:27:291

关于ThreadLocal如何保证数据线程安全

ThreadLocal的核心概念是没一个线程可以通过get或者set方法访问它自己的、独立初始化的变量的副本。1. ThreadLocal介绍为了在多线程环境下不出现任何的冲突,我们希望能否分离一个类的多个实例。对于每一个线程来说,没一个实例都是唯一的。这不过是实现线程安全的一个方式。线程安全的另外的重要的一点是能够全局访问。可以在线程内部的任何地方进行访问。记住:应该声明成static和final的。2. 什么是线程安全线程是进程的一条单独的线。当我们提到多线程应用的时候,我们的意思是一个进程的多个线程访问同一行代码。在这个情况下,存在一个线程访问或者修改另一个线程的数据的可能性。当数据不允许这样共享的时候,我们应该做成线程安全的。实现线程安全的方式有下面几种:· 重入·互斥(同步机制)·ThreadLocal·原子操作3. 使用ThreadLocal(引用Joshua Bloch的一段话,巨抽象,我没看懂,......)我们有一个非线程安全的变量,我们想把它变成线程安全的,可以考虑同步机制把对象封闭到同步块中。另外的方式是使用ThreadLocal,为每一个线程持有单独的对象使得变得安全。4. ThreadLocal的例子package com.javapapers;import java.text.SimpleDateFormat;import java.util.Date;public class ThreadLocalExample {private static final ThreadLocal formatter = new ThreadLocal() {protected SimpleDateFormat initialValue() {return new SimpleDateFormat("yyyyMMdd HHmm");}};public String formatIt(Date date) {return formatter.get().format(date);}}上边的代码中,关键要理解get()方法。他返回的当前线程的ThreadLocal变量的副本。如果当前线程的这个变量还没有值,则通过调用initalValue方法返回第一次初始化的值。JavaDoc中的例子下面的例子为每一个线程生成一个唯一的标识符。第一次调用get方法的时候指定一个线程的id,并且在随后的调用中保持不变。port java.util.concurrent.atomic.AtomicInteger;public class ThreadId {// Atomic integer containing the next thread ID to be assignedprivate static final AtomicInteger nextId = new AtomicInteger(0);// Thread local variable containing each thread"s IDprivate static final ThreadLocal threadId =new ThreadLocal() {@Override protected Integer initialValue() {return nextId.getAndIncrement();}};// Returns the current thread"s unique ID, assigning it if necessarypublic static int get() {return threadId.get();}}5. Java API中ThreadLocal 的使用在JDK1.7中,我们有一个叫做ThreadLocalRandom的新类。它用于为并行的线程生成随机数,每一个线程是随机数的种子都是唯一的。这是一个非常酷的功能。下面的代码是上边的类实现ThreadLocal 的代码:private static final ThreadLocal localRandom =new ThreadLocal() {protected ThreadLocalRandom initialValue() {return new ThreadLocalRandom();}};使用ThreadLocalRandompackage com.javapapers;import java.util.concurrent.ThreadLocalRandom;public class ThreadLocalRandomExample {public static void main(String args[]) throws InterruptedException {//tossing 3 coinsfor (int i = 0; i < 3; i++) {final Thread thread = new Thread() {public void run() {System.out.print(Thread.currentThread().getName() + ":");// generating 3 random numbers - random for every threadfor (int j = 0; j < 3; j++) {final int random = ThreadLocalRandom.current().nextInt(1, 3);System.out.print(random + ",");}System.out.println();}};thread.start();thread.join();}}}6. ThreadLocal和内存泄露ThreadLocal不是一个魔鬼,而是一个优秀的、实用的API。完全取决于我们怎么样去使用。我们应该学会在正确的场合使用合适的工具。我们不能使用大炮打蚊子,但是也不能谴责大炮。
2023-07-01 08:27:361

dubbo 中使用 ThreadLocal 隐患

在 Dubbo 中使用 ThreadLocal ,如果采用默认的设置,每次 Dubbo 调用结束,Dubbo 处理响应线程并不会被销毁, 而是归还到线程池中。而从 ThreadLocal 源码可以看出,每次我们设置的值其实会存在位于 Thread 中 ThreadLocalMap 变量中。 这就导致,下次如果 Dubbo 处理响应恰好继续使用到这个线程,该线程就能调用到上次响应中设置在 ThreadLocal 设置的值。这就引起内存泄露,可能还会导致业务上异常。其实并不止在 Dubbo 中,该案例还会发生在 web项目中,只要相关使用线程池的,都有可能发生。 dubbo 默认采用单一长连接加线程池方式处理调用。 默认采取 Dispatcher=all 的分发策略,所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。 线程池在缺省配置为固定大小线程池,启动时建立线程,不关闭,一直持有。 使用 Threadlocal,我们需要注意几点: 调整线程池大小配置是 dubbo.protocol.threads = 5000 调整线程池类型配置是 dubbo.protocol.threadpool = cached 调整事件处理方式配置是 dubbo.protocol.dispatcher = message 或者 <dubbo:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="100" />
2023-07-01 08:27:421

ThreadLocal 和 Thread 局部变量的区别

ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量。 ThreadLocal是Thread的局部变量。
2023-07-01 08:27:501

java threadlocal线程结束会释放当前线程的数据吗?

不会清空,要你自己去清空。只有当ThreadLocal的生命周期受限于Task的生命周期时,在Thread Pool的Thread里使用ThreadLocal才有意义。Task指的是一个Thread所执行的任务。总之,如果你能够在使用ThreadLocal的时候管理它的创建、销毁,那么就可以用,否则会出问题。原因是ThreadLocal是和Thread绑定的,如果Thread是从Thread Pool中拿出来的,那么意味着Thread可能会被复用,如果被复用,你就一定得保证这个Thread上一次结束的时候,其关联的ThreadLocal被清空掉,否则就会串到下一次使用。
2023-07-01 08:27:561

c++有threadlocal么

C++有thread_local关键字,不过得在新版本的编译器才能使用。
2023-07-01 08:28:141

如何将ThreadLocal传递到子线程

ThreadLocal是保证在同一个线程内共享,而不同线程的实例是不同的。 如果想在不同线程内共享,那么直接用公共静态属性即可,如: public static int pagesize;
2023-07-01 08:28:201

解决线程切换导致ThreadLocal值丢失

1.jdk 自带的InheritableThreadLocal 只针对父子线程 有效,针对线程池失效 2.使用阿里的transmittable-thread-local 可以解决线程池内的threadLocal 问题 只需要修改2个地方,修饰线程池和替换InheritableThreadLocal: 原文地址请点击此处
2023-07-01 08:28:271

什么是线程安全以及threadlocal为什么是线

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。线程安全就是说多线程访问同一代码,不会产生不确定的结果。编写线程安全的代码是依靠线程同步。产生不确定 结果的例子:定义一个共享数据: public static int a = 0;多线程多该共享数据进行修改: private static void plus() { for (int i = 0; i < 10; i++) { new Thread() { public void run() { a++; try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("plus:" + Thread.currentThread().getName() + ": " + a); } }.start(); }}输出结果: plus:Thread-5: 5plus:Thread-2: 5plus:Thread-6: 5plus:Thread-9: 5plus:Thread-1: 6plus:Thread-0: 8plus:Thread-4: 8plus:Thread-3: 10plus:Thread-7: 10plus:Thread-8: 10很明显,在第一次输出a的值的时候,a的值就已经被其他线程修改到5了,显然线程不安全。 产生确定结果的例子:利用synchronized关键字将修改a值的地方和输出的地方上锁。让这段代码在某一个时间段内始终只有一个 线程在执行: private static void plus() { for (int i = 0; i < 10; i++) { new Thread() { public void run() { synchronized (JavaVariable.class) { a++; try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("plus:" + Thread.currentThread().getName() + ": " + a); } } }.start(); }}输出结果: plus:Thread-2: 1plus:Thread-6: 2plus:Thread-7: 3plus:Thread-3: 4plus:Thread-8: 5plus:Thread-4: 6plus:Thread-0: 7plus:Thread-9: 8plus:Thread-5: 9plus:Thread-1: 10结果正确。 为什么threadlocal是线程安全的?首先定义一个ThreadLocal。 private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {protected Integer initialValue() {return 0;}};插一句题外话:为什么threadLocal要被声明成静态的?这是为了多个线程使用同一个ThreadLocal对象。一个线程对应一个ThreadLocal对象,和多个线程使用同一个ThreadLocal对象结果是一样的。详细情况参考链接:http://www.blogjava.net/zhangwei217245/archive/2010/04/24/317651.html接着上面的话题,也许可以这样: private static void plus() throws Exception { for (int i = 0; i < 10; i++) { new Thread() { public void run() { //1 a = threadLocal.get(); a++; //2 threadLocal.set(a); System.out.println("plus:" + Thread.currentThread().getName() + ": " + threadLocal.get()); } }.start(); }}它的结果是怎样呢?全部会输出1。当然,它无法使多个线程共同修改一个值,并且保持这个值递增。因为threadlocal不是做这个的,它是为了隔离数据的共享,而不是像同步机制一样实现线程间的通信。但它的结果是可以确定的,所以它是线程安全的。很明显,在代码“1”的时候,每一个线程都会将threadLocal的初始值0赋值给共享变量a,因为每一个线程从threadLocal.get()拿到的值都是自己threadLocal保存的。所以,就没有所以了。 所以对于共享变量a来讲,每个线程都会首先将自己threadLocal里面的初始值0赋值给a,然后将共享变量a+1,然后将a+1的值设置到自己的ThreadLocalMap中,其他线程就访问不到了。下一个线程来的时候又会将自己threadLocal里面的初始值0赋值给a,然后将 a+1,然后... 如此周而复始。a只是被在0和1之间改来改去,最终放到每一个线程的threadLocal里面的a+1的值就不再共享。
2023-07-01 08:28:341

java中ThreadLocal是哪个包的

JDK1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量。
2023-07-01 08:28:411

PageHelper 使用 ThreadLocal 的线程复用问题,你用对了吗?

PageHelper 是较为常用的分页插件,通过实现 Mybatis 的 Interceptor 接口完成对 query sql 的动态分页,其中分页参数由 ThreadLocal 进行保存。 简单的 分页执行过程: 观察上述的执行过程,可以发现,如果在第 1 步和第 2 步 之间发生异常,那么 LOCAL_PAGE 中当前线程对应的 page 参数并不会 remove。 在不使用线程池的情况下,当前线程在执行完毕后会被销毁,这时 当前线程 中的 threadLocals 参数 将会被情况,也就清空 了 LOCAL_PAGE 中 当前线程的 page 参数。 但是如果使用了线程池,当前线程执行完毕,并不会被销毁,而是会将当前线程再次存放到池中,标记为空闲状态,以便后续使用。在后续使用这个线程的时候,由于 线程 的 threadLocals 依旧存在有值,尽管我们在第 1 步时未设置 page 参数,第 3 步 的也能获取到page参数,从而生成 count sql 和 page sql ,从而影响我们的正常查询。 SpringBoot 项目中会使用内置的 Tomcat 作为服务器,而Tomcat会默认使用线程来处理请求,从而便引发了上述问题。 因为Tomcat的线程是用来处理request请求,那么在请求完成时,清空当前线程的threadLocals 属性值,也就是执行 LOCAL_PAGE.remove() 即可。实现方式: 这里使用第二种方式,实现 HandlerInterceptor 接口: 定义配置类,配置类需实现 WebMvcConfigurer 接口完成对于WebMvc的相关配置 ,注册 PageLocalWebInterceptor :
2023-07-01 08:28:471

java ThreadLocal空异常

你在getT1 能获得值 的地方 和getT1 获得的值是null 的地方 都打上一句话 System.out.println(Thread.currentThread());他们输出的线程 肯定不是一个线程然后你打开get () 这个方法会发现,获取的Threadlocal的value 是当前线程的value,意思是 前者是你设置value 的线程 你能获得这个value 后者不是该线程 故得到的value 是null
2023-07-01 08:28:561

ThreadLocal 传递参数的问题

ThreadLocal是保证在同一个线程内共享,而不同线程的实例是不同的。如果想在不同线程内共享,那么直接用公共静态属性即可,如:public static int pagesize;
2023-07-01 08:29:141

项目中怎么控制多线程高并发访问

1、首先明确信号量Semaphore的用法,然后新建一个项目,new-->file-->class,随意命名,此处命名为semaphoreDemo。2、首先开始一个线程MyTask,实现接口,然后在其中定义窗口买票的流程,主要有进入,买完了,离开,释放信号量,让下一个进入。3、然后设定一个信号量,主要是执行函数,此处定义窗口个数,定义线程池ExecutorService,循环执行这20个人。4、最后通过main函数调用execute函数进行排队问题,开始排队线程。5、在打印中可以看到两个线程在并发执行,剩下的人处于排队状态,只有上一个执行完了下一个才执行。
2023-07-01 08:29:213

JAVA线程本地变量ThreadLocal和私有变量的区别

ThreadLocal变量 作用域是各自线程内部。私有变量作用域 属于该类的实例。所以, ThreadLocal变量 只用于线程内部共享,是线程安全的。私有变量线程不安全,例如,利用一个Runnable实例启动2个线程,这2个线程就可以共同拥有 私有变量。
2023-07-01 08:30:451

threadlocal存在线程安全问题吗

ThreadLocal是线程安全的,它为解决线程安全问题提供一个很好的思路,通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。
2023-07-01 08:30:521

ThreadLocal类为什么要加上static修饰

因为对于非静态成员函数而言,默认情况下,参数列表中都会有一个this指针,例如fun(自定义参数),实际上编译后就变成这样类型:fun(自定义参数,某个类 * this)。这样编译就会出错,多了一个参数,所以这个函数就不能作为线程函数了。加上static修饰之后,类的成员函数就不会加上默认this了,所以符合调用规定。
2023-07-01 08:30:592

threadlocal为什么entry的key要设置成弱引用

首先弱引用在垃圾回收的时候,即使存在弱引用,也会回收引用的对象。 看过threadlocal源码的同学肯定知道,threadlocal内部就是在线程Thread类中new了一个类map结构,然后里面存储数据也是用entry。 看源码会发现entry的key是一个弱引用,为什么要这样设计呢? 不一定会内存泄露的。 现在entry的key对应的实例对象能正常销毁了。那么entry对应的值能正常销毁吗?
2023-07-01 08:31:061

请教一下,Java局部变量为什么一定要用ThreadLocal?

举个例子吧,好比俩售票员卖票使用同步机制(synchronized),相当于只有一个计数器,甲卖票的时候先锁定,卖好了,钱也交完了,计数器+1,再解锁,这功夫要是乙那也有业务,就得等会。只要一方锁定,就必须等所有业务都完事才能解锁(卖票,首款,打印单据blabla)。ThreadLocal,就相当于每人一个计数器,不用担心另一个售票员把我统计的票数改错了。就是这么个用处。
2023-07-01 08:31:211

如何获得thread线程的threadlocals的key值

当前的thread线程是获取不到的,threadLocals的维护在jdk中说明了,是由threadLocal来维护的。如果应该从threadLocal中获取threadLocals的值。如果想从当前的thread线程中获取的话,可以考虑使用缓存;例如,以当前线程唯一属性或id,存入缓存中,当需要用时从缓存中拿。
2023-07-01 08:31:282

ThreadLocal的使用方法是什么?

早在Java 1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序时提供了一种新的选择。使用这个工具类可以很简洁地编写出优美的多线程程序,虽然ThreadLocal非常有用,但是似乎现在了解它、使用它的朋友还不多。    ThreadLocal是什么    ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。线程局部变量并不是Java的新发明,在其它的一些语言编译器实现(如IBM XL FORTRAN)中,它在语言的层次提供了直接的支持。因为Java中没有提供在语言层次的直接支持,而是提供了一个ThreadLocal的类来提供支持,所以,在Java中编写线程局部变量的代码相对比较笨拙,这也许是线程局部变量没有在Java中得到很好的普及的一个原因吧。    ThreadLocal的设计    首先看看ThreadLocal的接口:     Object get() ; // 返回当前线程的线程局部变量副本 protected Object initialValue(); // 返回该线程局部变量的当前线程的初始值     void set(Object value); // 设置当前线程的线程局部变量副本的值    ThreadLocal有3个方法,其中值得注意的是initialValue(),该方法是一个protected的方法,显然是为了子类重写而特意实现的。该方法返回当前线程在该线程局部变量的初始值,这个方法是一个延迟调用方法,在一个线程第1次调用get()或者set(Object)时才执行,并且仅执行1次。ThreadLocal中的确实实现直接返回一个null:   protected Object initialValue() { return null; }   ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现:   public class ThreadLocal   {    private Map values = Collections.synchronizedMap(new HashMap());    public Object get()    {    Thread curThread = Thread.currentThread();    Object o = values.get(curThread);    if (o == null && !values.containsKey(curThread))    {     o = initialValue();     values.put(curThread, o);    }    return o;    }    public void set(Object newValue)    {    values.put(Thread.currentThread(), newValue);    }    public Object initialValue()    {    return null;    }   }   当然,这并不是一个工业强度的实现,但JDK中的ThreadLocal的实现总体思路也类似于此。    ThreadLocal的使用    如果希望线程局部变量初始化其它值,那么需要自己实现ThreadLocal的子类并重写该方法,通常使用一个内部匿名类对ThreadLocal进行子类化,比如下面的例子,SerialNum类为每一个类分配一个序号:   public class SerialNum   {    // The next serial number to be assigned    private static int nextSerialNum = 0;    private static ThreadLocal serialNum = new ThreadLocal()    {    protected synchronized Object initialValue()    {     return new Integer(nextSerialNum++);    }    };    public static int get()    {    return ((Integer) (serialNum.get())).intValue();    }   }   SerialNum类的使用将非常地简单,因为get()方法是static的,所以在需要获取当前线程的序号时,简单地调用:   int serial = SerialNum.get();   即可。    在线程是活动的并且ThreadLocal对象是可访问的时,该线程就持有一个到该线程局部变量副本的隐含引用,当该线程运行结束后,该线程拥有的所以线程局部变量的副本都将失效,并等待垃圾收集器收集。    ThreadLocal与其它同步机制的比较    ThreadLocal和其它同步机制相比有什么优势呢?ThreadLocal和其它所有的同步机制都是为了解决多线程中的对同一变量的访问冲突,在普通的同步机制中,是通过对象加锁来实现多个线程对同一变量的安全访问的。这时该变量是多个线程共享的,使用这种同步机制需要很细致地分析在什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放该对象的锁等等很多。所有这些都是因为多个线程共享了资源造成的。ThreadLocal就从另一个角度来解决多线程的并发访问,ThreadLocal会为每一个线程维护一个和该线程绑定的变量的副本,从而隔离了多个线程的数据,每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的整个变量封装进ThreadLocal,或者把该对象的特定于线程的状态封装进ThreadLocal。    由于ThreadLocal中可以持有任何类型的对象,所以使用ThreadLocal get当前线程的值是需要进行强制类型转换。但随着新的Java版本(1.5)将模版的引入,新的支持模版参数的ThreadLocal类将从中受益。也可以减少强制类型转换,并将一些错误检查提前到了编译期,将一定程度地简化ThreadLocal的使用。    总结    当然ThreadLocal并不能替代同步机制,两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;而ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不需要对多个线程进行同步了。所以,如果你需要进行多个线程之间进行通信,则使用同步机制;如果需要隔离多个线程之间的共享冲突,可以使用ThreadLocal,这将极大地简化你的程序,使程序更加易读、简洁。
2023-07-01 08:31:461

threadlocal set方法是不是线程安全的

ThreadLocal的核心概念是没一个线程可以通过get或者set方法访问它自己的、独立初始化的变量的副本。1. ThreadLocal介绍为了在多线程环境下不出现任何的冲突,我们希望能否分离一个类的多个实例。对于每一个线程来说,没一个实例都是唯一的。这不过是实现线程安全的一个方式。线程安全的另外的重要的一点是能够全局访问。可以在线程内部的任何地方进行访问。记住:应该声明成static和final的。2. 什么是线程安全线程是进程的一条单独的线。当我们提到多线程应用的时候,我们的意思是一个进程的多个线程访问同一行代码。在这个情况下,存在一个线程访问或者修改另一个线程的数据的可能性。当数据不允许这样共享的时候,我们应该做成线程安全的。实现线程安全的方式有下面几种:· 重入·互斥(同步机制)·ThreadLocal·原子操作3. 使用ThreadLocal(引用Joshua Bloch的一段话,巨抽象,我没看懂,......)我们有一个非线程安全的变量,我们想把它变成线程安全的,可以考虑同步机制把对象封闭到同步块中。另外的方式是使用ThreadLocal,为每一个线程持有单独的对象使得变得安全。4. ThreadLocal的例子package com.javapapers;import java.text.SimpleDateFormat;import java.util.Date;public class ThreadLocalExample { private static final ThreadLocal formatter = new ThreadLocal() { protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyyMMdd HHmm"); } }; public String formatIt(Date date) { return formatter.get().format(date); }}上边的代码中,关键要理解get()方法。他返回的当前线程的ThreadLocal变量的副本。如果当前线程的这个变量还没有值,则通过调用initalValue方法返回第一次初始化的值。JavaDoc中的例子下面的例子为每一个线程生成一个唯一的标识符。第一次调用get方法的时候指定一个线程的id,并且在随后的调用中保持不变。port java.util.concurrent.atomic.AtomicInteger;public class ThreadId { // Atomic integer containing the next thread ID to be assigned private static final AtomicInteger nextId = new AtomicInteger(0); // Thread local variable containing each thread"s ID private static final ThreadLocal threadId = new ThreadLocal() { @Override protected Integer initialValue() { return nextId.getAndIncrement(); } }; // Returns the current thread"s unique ID, assigning it if necessary public static int get() { return threadId.get(); }}5. Java API中ThreadLocal 的使用在JDK1.7中,我们有一个叫做ThreadLocalRandom的新类。它用于为并行的线程生成随机数,每一个线程是随机数的种子都是唯一的。这是一个非常酷的功能。下面的代码是上边的类实现ThreadLocal 的代码:private static final ThreadLocal localRandom = new ThreadLocal() { protected ThreadLocalRandom initialValue() { return new ThreadLocalRandom(); }};使用ThreadLocalRandompackage com.javapapers;import java.util.concurrent.ThreadLocalRandom;public class ThreadLocalRandomExample { public static void main(String args[]) throws InterruptedException { //tossing 3 coins for (int i = 0; i < 3; i++) { final Thread thread = new Thread() { public void run() { System.out.print(Thread.currentThread().getName() + ":"); // generating 3 random numbers - random for every thread for (int j = 0; j < 3; j++) { final int random = ThreadLocalRandom.current().nextInt( 1, 3); System.out.print(random + ","); } System.out.println(); } }; thread.start(); thread.join(); } }}6. ThreadLocal和内存泄露ThreadLocal不是一个魔鬼,而是一个优秀的、实用的API。完全取决于我们怎么样去使用。我们应该学会在正确的场合使用合适的工具。我们不能使用大炮打蚊子,但是也不能谴责大炮。
2023-07-01 08:32:001

threadlocal有没有继承thread类

ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量。ThreadLocal是Thread的局部变量。所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。ThreadLocal的接口方法:ThreadLocal类接口很简单,只有4个方法,先来了解一下:voidset(Objectvalue)publicvoidremove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK1.5新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。protectedObjectinitialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal。API方法也相应进行了调整,新版本的API方法分别是voidset(Tvalue)、Tget()以及TinitialValue()。ThreadLocal为每一个线程维护变量的副本的思路:在ThreadLocal类中定义了一个ThreadLocalMap,每一个Thread中都有一个该类型的变量——threadLocals——用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。
2023-07-01 08:32:061

ThreadLocal 和 Thread 局部变量的区别

ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量。 ThreadLocal是Thread的局部变量。
2023-07-01 08:32:131

如何获得thread线程的threadlocalmap成员属性

dLocalMap threadlocalmap = getMap(thread); if(threadlocalmap != null) { ThreadLocalMap.Entry entry = threadlocalmap.getEntry(this); if(entry != null)
2023-07-01 08:32:191

ThreadLocal 和 Thread 局部变量的区别

```javapublic static void main(String[] args) {MyThread myThread = new MyThread("oo");new Thread(myThread,"1").start();new Thread(myThread,"2").start();}static class MyThread implements Runnable {private String index;public MyThread(String index) {this.index = index;}//局部变量private Integer num = 0;public void run() {System.out.println("线程" + Thread.currentThread().getName() +index+ "的初始 num:" + num);System.out.println("线程" + Thread.currentThread().getName()+index + "的初始 value:" + value.get());for (int i = 0; i < 10; i++) {num += i;value.set(value.get() + i);}System.out.println("线程" + Thread.currentThread().getName() +index + "的累加 num:" + num);System.out.println("线程" + Thread.currentThread().getName() +index + "的累加 value:" + value.get());}}```结果:```线程 2oo 的初始 num:0线程 1oo 的初始 num:0线程 2oo 的初始 value:0线程 1oo 的初始 value:0线程 2oo 的累加 num:45线程 1oo 的累加 num:90线程 1oo 的累加 value:45线程 2oo 的累加 value:45```结论:自定义私有局部变量 线程不安全,当上述情况时
2023-07-01 08:32:261

一个线程结束时threadlocal里面的池的连接会自己关闭么

ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量。 ThreadLocal是Thread的局部变量。所以,在Java中编写线程局部变量的代码...
2023-07-01 08:32:331

java threadlocal线程结束会释放当前线程的数据吗?

不会清空,要你自己去清空。只有当ThreadLocal的生命周期受限于Task的生命周期时,在Thread Pool的Thread里使用ThreadLocal才有意义。Task指的是一个Thread所执行的任务。总之,如果你能够在使用ThreadLocal的时候管理它的创建、销毁,那么就可以用,否则会出问题。原因是ThreadLocal是和Thread绑定的,如果Thread是从Thread Pool中拿出来的,那么意味着Thread可能会被复用,如果被复用,你就一定得保证这个Thread上一次结束的时候,其关联的ThreadLocal被清空掉,否则就会串到下一次使用。
2023-07-01 08:32:411

RequestFilter.threadLocal.get().getSession()和request.getSession()的区别?

request.getSession(true):若存在会话则返回该会话,否则新建一个会话。request.getSession(false):若存在会话则返回该会话,否则返回NULL
2023-07-01 08:32:591

threadlocal提供了delete方法

你想问的是threadlocal是否提供了delete方法对吧?没有的。threadlocal而是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据,hreadLocal提供了线程内存储变量的能力,这些变量不同之处在于每一个线程读取的变量是对应的互相独立的。通过get和set方法就可以得到当前线程对应的值
2023-07-01 08:33:061

java中threadlocal是线程安全的吗

你可以这样理解:ThreadLocal是线程安全的,session具有唯一性
2023-07-01 08:33:131

java threadlocal 线程结束会释放当前线程的数据吗

after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).官方解释,翻译一下就是:在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。
2023-07-01 08:33:212

threadlocal的remove方法会阻塞吗

threadlocal的remove方法不会阻塞。threadlocal调用remove方法会导致业务逻辑通顺,web容器使用了线程池,当一个请求使用完某个线程,该线程会放回线程池被其它请求使用。所以threadlocal的remove方法不会阻塞。
2023-07-01 08:33:281

集群环境下,能用ThreadLocal吗

这个不用多想肯定不会的。1、集群只是对请求一个分发。负载均衡而已。2、一个请求过来,根据分发策略然后进入其中一台服务器。进入这个服务器之后就没有分发策略了。只会在这台服务器执行完这个线程。
2023-07-01 08:33:352

如何将ThreadLocal传递到子线程

ThreadLocal是保证在同一个线程内共享,而不同线程的实例是不同的。 如果想在不同线程内共享,那么直接用公共静态属性即可,如: public static int pagesize;
2023-07-01 08:33:411

Spring 单例 多例 线程安全等问题,想请教大家

Spring作为一个IOC/DI容器,帮助我们管理了许许多多的“bean”。但其实,Spring并没有确保这些对象的线程安全,需要由开发者自己编写解决线程安全问题的代码。Spring对每个bean提供了一个scope属性来表示该bean的作用域。它是bean的生命周期。我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程。ThreadLocal是解决线程安全问题一个很好的思路,ThreadLocal是一个为线程提供线程局部变量的工具类。它的思想也十分简单,就是为线程提供一个线程私有的变量副本,这样多个线程都可以随意更改自己线程局部的变量,不会影响到其他线程。不过需要注意的是,ThreadLocal提供的只是一个浅拷贝,如果变量是一个引用类型,那么就要考虑它内部的状态是否会被改变,想要解决这个问题可以通过重写ThreadLocal的initialValue()函数来自己实现深拷贝,建议在使用ThreadLocal时一开始就重写该函数。ThreadLocal通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。
2023-07-01 08:33:482

静态线程池对象不关闭会怎样

我们知道Spring通过各种DAO模板类降低了开发者使用各种数据持久技术的难度。这些模板类都是线程安全的,也就是说,多个DAO可以复用同一个模板实例而不会发生冲突。我们使用模板类访问底层数据,根据持久化技术的不同,模板类需要绑定数据连接或会话的资源。但这些资源本身是非线程安全的,也就是说它们不能在同一时刻被多个线程共享。虽然模板类通过资源池获取数据连接或会话,但资源池本身解决的是数据连接或会话的缓存问题,并非数据连接或会话的线程安全问题。按照传统经验,如果某个对象是非线程安全的,在多线程环境下,对对象的访问必须采用synchronized进行线程同步。但Spring的DAO模板类并未采用线程同步机制,因为线程同步限制了并发访问,会带来很大的性能损失。此外,通过代码同步解决性能安全问题挑战性很大,可能会增强好几倍的实现难度。那模板类究竟仰丈何种魔法神功,可以在无需同步的情况下就化解线程安全的难题呢?答案就是ThreadLocal!ThreadLocal在Spring中发挥着重要的作用,在管理request作用域的Bean、事务管理、任务调度、AOP等模块都出现了它们的身影,起着举足轻重的作用。要想了解Spring事务管理的底层技术,ThreadLocal是必须攻克的山头堡垒。ThreadLocal是什么早在JDK1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。线程局部变量并不是Java的新发明,很多语言(如IBM IBM XLFORTRAN)在语法层面就提供线程局部变量。在Java中没有提供在语言级支持,而是变相地通过ThreadLocal的类提供支持。所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。ThreadLocal的接口方法ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:void set(Object value)设置当前线程的线程局部变量的值。public Object get()该方法返回当前线程所对应的线程局部变量。public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是voidset(T value)、T get()以及T initialValue()。ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。我们自己就可以提供一个简单的实现版本:// 代码清单1 SimpleThreadLocalclass SimpleThreadLocal {private MapvalueMap = Collections.synchronizedMap(new HashMap());public voidset(Object newValue) {valueMap.put(Thread.currentThread(), newValue);//①键为线程对象,值为本线程的变量副本}publicObject get() {Thread currentThread = Thread.currentThread();Object o = valueMap.get(currentThread);// ②返回本线程对应的变量if (o == null &&!valueMap.containsKey(currentThread)) {// ③如果在Map中不存在,放到Map// 中保存起来。o = initialValue();valueMap.put(currentThread, o);}return o;}public voidremove() {valueMap.remove(Thread.currentThread());}publicObject initialValue() {return null;}}虽然代码清单9?3这个ThreadLocal实现版本显得比较幼稚,但它和JDK所提供的ThreadLocal类在实现思路上是相近的。一个TheadLocal实例下面,我们通过一个具体的实例了解一下ThreadLocal的具体使用方法package threadLocalDemo;public class SequenceNumber {//①通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值privatestatic ThreadLocal<Integer> seqNum =new ThreadLocal<Integer>() {public Integer initialValue() {return 0;}};//②获取下一个序列值public intgetNextNum() {seqNum.set(seqNum.get() + 1);return seqNum.get();}publicstatic void main(String[] args){SequenceNumber sn = new SequenceNumber();// ③ 3个线程共享sn,各自产生序列号TestClient t1 = new TestClient(sn);TestClient t2 = new TestClient(sn);TestClient t3 = new TestClient(sn);t1.start();t2.start();t3.start();}privatestatic class TestClient extends Thread{private SequenceNumber sn;public TestClient(SequenceNumber sn) {this.sn = sn;}public void run(){for (int i = 0; i < 3; i++) {// ④每个线程打出3个序列值System.out.println("thread[" + Thread.currentThread().getName()+"]sn[" + sn.getNextNum() + "]");}}}}通常我们通过匿名内部类的方式定义ThreadLocal的子类,提供初始的变量值,如例子中①处所示。TestClient线程产生一组序列号,在③处,我们生成3个TestClient,它们共享同一个SequenceNumber实例。运行以上代码,在控制台上输出以下的结果:thread[Thread-2] sn[1]thread[Thread-0] sn[1]thread[Thread-1] sn[1]thread[Thread-2] sn[2]thread[Thread-0] sn[2]thread[Thread-1] sn[2]thread[Thread-2] sn[3]thread[Thread-0] sn[3]thread[Thread-1] sn[3]考察输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个SequenceNumber实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。Thread同步机制的比较ThreadLocal和线程同步机制相比有什么优势呢?ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK5.0通过泛型很好的解决了这个问题,在一定程度地简化ThreadLocal的使用,代码清单 9 2就使用了JDK5.0新的ThreadLocal<T>版本。概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。Spring使用ThreadLocal解决线程安全问题我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程,如图9?2所示:图1同一线程贯通三层这样你就可以根据需要,将一些非线程安全的变量以ThreadLocal存放,在同一次请求响应的调用线程中,所有关联的对象引用到的都是同一个变量。下面的实例能够体现Spring对有状态Bean的改造思路:代码清单3 TopicDao:非线程安全public class TopicDao {private Connection conn;①一个非线程安全的变量public void addTopic(){Statement stat = conn.createStatement();②引用非线程安全变量…}}由于①处的conn是成员变量,因为addTopic()方法是非线程安全的,必须在使用时创建一个新TopicDao实例(非singleton)。下面使用ThreadLocal对conn这个非线程安全的“状态”进行改造:代码清单4 TopicDao:线程安全package threadLocalDemo;import java.sql.Connection;import java.sql.SQLException;import java.sql.Statement;public class SqlConnection {//①使用ThreadLocal保存Connection变量privatestatic ThreadLocal<Connection>connThreadLocal = newThreadLocal<Connection>();publicstatic Connection getConnection() {// ②如果connThreadLocal没有本线程对应的Connection创建一个新的Connection,// 并将其保存到线程本地变量中。if (connThreadLocal.get() == null) {Connection conn = getConnection();connThreadLocal.set(conn);return conn;} else {return connThreadLocal.get();// ③直接返回线程本地变量}}public voidaddTopic() {// ④从ThreadLocal中获取线程对应的Connectiontry {Statement stat = getConnection().createStatement();} catch (SQLException e) {e.printStackTrace();}}}
2023-07-01 08:33:551