ThreadLocal

什么是ThreadLocal?

 

        使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。从线程的角度看,目标变量就像是线程的本地变量,这也是类名中“Local”所要表达的意思。

ThreadLocal的作用

线程并发:在多线程并发的场景下

传递数据:通过ThreadLocal在同一线程,不同组件传递公共变量。

线程隔离:每个线程的变量都是独立的,不会相互影响

ThreadLocal常用的方法

方法申明描述
ThreadLocal()创建ThreadLocal对象
public void set(T value)设置当前线程绑定的局部变量
public T get()获取当前线程绑定的局部变量
public void remove()移除当前线程绑定的局部变量

案例演示:

ThreadLocal方法

public class ThreadDemo {

    ThreadLocal<String>  t = new ThreadLocal<>();
    private String content;

    public void setContent(String content) {
        t.set(content);
    }

    public String getContent() {
        return t.get();
    }

    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();
        for (int i = 0; i <5 ; i++) {
           Thread thread =  new Thread(new Runnable() {
               @Override
               public void run() {
                   threadDemo.setContent(Thread.currentThread().getName()+"的数据");
                   System.out.println("--------------------");
                   System.out.println( Thread.currentThread().getName()+"线程"+"--->"+threadDemo.getContent());

               }
           });
           thread.start();
        }
    }
}

 synchronized方法: 

public class ThreadDemo {

    
    private String content;

    public void setContent(String content) {

        this.content = content;
    }

    public String getContent() {
        return content;
    }

    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();
        for (int i = 0; i <5 ; i++) {
           Thread thread =  new Thread(new Runnable() {
               @Override
               public void run() {
                   synchronized (ThreadDemo.class){
                       threadDemo.setContent(Thread.currentThread().getName()+"的数据");
                       System.out.println("--------------------");
                       System.out.println( Thread.currentThread().getName()+"线程"+"--->"+threadDemo.getContent());
                   }
               }
           });
           thread.start();
        }
    }
}

        虽然说 synchronized也能解决这个问题,但是,在这里我们侧重解决的是线程的数据隔离问题,而不是共享变量的问题。所以使用 synchronized不太合适。 

ThreadLocal与Synchronized的区别

 synchronizedThreadLocal
原理同步机制,采用时间换空间的方式,让不同的线程排队等候。以空间换时间的方式,给每个线程都给了一个变量副本,从而实现同时访问,线程之间互不干扰。
侧重点解决线程之间的共享变量问题线程之间的数据隔离

 运用场景:

转账的JDBC的connection问题:JDBC Connection 类是非线程安全的,两个线程不能安全地共享一个 Connection。

当线程A获取到Connection,开启一个事务,正在在执行事务,但是未结束。此时,线程B也获取到Connection,它发送了一些SQL操作,这些SQL操作将会被数据库归入线程A的事务当中被执行,这就造成了张冠李戴。

如果采用线程局部变量的形式,此时Connection为线程A和线程B的局部变量,本质上就是开启了两个Connection,从而可以使线程A和线程B可以相关独立。

service层与dao层的connection是同一个,这样就不会导致出现问题。我们在创建JDBCUtil时,就将ThreadLocal申明出来

在线程并发情况下每个线程都只能操作自己的connection。

ThreadLocal原理

Thread中维护了一个ThreadLocalMap, ThreadLocalMap的Key就是ThreadLocal,线程中的局部变量就是value的值。

具体描述:

  1. 每个Thread线程内部都有一个Map(ThreadLocalMap)
  2. Map里面存储ThreadLocal对象(Key)和线程的变量副本(value)
  3. Thread内部的Map是由ThreadLocal维护的,由ThreadLocal向Map获取和设置线程的变量值。
  4. 对于不同的线程,每次获取副本值时,别的线程并不能获取到当前线程的副本值,形成了副本的隔离,互不干扰。

 下面是JDK的早期设计

 JDK8的设计方案两个好处:

  • 每个Map存储的Entry数量变少
  • 当每个Thread销毁的时候,ThreadLocalMap也会随之销毁,减少内存的使用。

 ThreadLocal的初始化方法:

返回当前线程对应的ThreadLocal的初始值。

此方法的第一次调用发生在,当线程通过get方法访问ThreadLocal值时;一般情况下,每个线程调用一次这个方法。

若是想ThreadLocal有一个不是null的初始值,我们就需要通过子类继承(@code  ThreadLocal)的方式去重写此方法,通过匿名内部类的方式实现。

protected T initiaValue(){
     return null
}

基本结构

 

 存储结构--Entry

源码展示

 Entry继承WeakReference。也就是key(ThreadLocal)是弱引用,其目的是ThreadLocal对象的生命周期和线程的生命周期解绑。

 

ThreadLocal为什么要使用弱引用

为了解决内存泄漏。

为什么用弱引用就能解决呢?

什么是内存泄漏?

内存中已动态分配的堆内存由于某种原因程序未释放或者无法释放,造成系统内存的浪费。

如果key使用强引用,会出现内存泄漏吗?

ThreadLocalRef使用完回收,接下来就是ThreadLocal,但是由于是强引用,所以无法回收。

完全无法避免内存泄漏的。

 如果key使用弱引用,会出现内存泄漏吗?

 ThreadLocalRef使用完回收;但是ThreadLocal使用的是弱连接,所以也可以没有连接到ThreadLocal的线程。我们将ThreadLocal回收即可。由于我们的Map是由Entry数据结构实现的,所以若是不能够删除,还是会造成内存泄漏的。

要避免内存泄漏的两种方法:

使用完ThreadLocal,调用其remove()方法删除对应的Entry

使用完ThreadLocal,当前线程Thread也随之运行结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值