20200409 ThreadLocal

ThreadLocal,并发编程

ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 

在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。ThreadLocal中有四个方法: 

1、get()方法是用来获取ThreadLocal在当前线程中保存的变量副本

2、set()用来设置当前线程中变量的副本

3、remove()用来移除当前线程中变量的副本

4、initialValue()是一个protected方法,显然是为了让子类覆盖而设计的,返回此线程局部变量的当前线程的初始值

 

初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化。

在进行get之前,必须先set,否则会报空指针异常;

 

很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。

 

ThreadLocal的使用方法和实现原理。

ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储

出于线程安全的考虑,必须将这段代码的两个方法进行同步处理,并且在调用connect的地方需要进行同步处理。

一个线程不需要关心其他线程是否对这个connect进行了修改的。

get()方法是用来获取ThreadLocal在当前线程中保存的变量副本,set()用来设置当前线程中变量的副本,remove()用来移除当前线程中变量的副本,initialValue()是一个protected方法,一般是用来在使用时进行重写的,它是一个延迟加载方法。

内部维护了一个ThreadLocalMap。

 

1)实际的通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中的;

2)为何threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,因为每个线程中可有多个threadLocal变量,就像上面代码中的longLocal和stringLocal;

3)在进行get之前,必须先set,否则会报空指针异常;

 

ThreadLocal是什么

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

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。

 

 

/**
 * Created on 2020/4/9 19:05
 * author:crs
 * Description: 测试ThreadLocal的使用
 */
public class TestThreadLocal {

    ThreadLocal intThread=  new ThreadLocal<Integer>();
    ThreadLocal strThread=  new ThreadLocal<String>();

   public void init(){
       //当前线程id和当前线程名称
       intThread.set(Thread.currentThread().getId());
       strThread.set(Thread.currentThread().getName());
    }

    public Long getLong(){
       return (Long) intThread.get();
    }

    public String getString(){
        return (String) strThread.get();
    }

    public static void main(String[] args) throws InterruptedException {
        TestThreadLocal testThreadLocal = new TestThreadLocal();
        testThreadLocal.init();
        System.out.println(testThreadLocal.getLong());
        System.out.println(testThreadLocal.getString());
        System.out.println("--->");
        Thread thread = new Thread(){
            @Override
            public void run() {
                //设置当前子线程的id和名称
                testThreadLocal.init();
                System.out.println(testThreadLocal.getLong());
                System.out.println(testThreadLocal.getString());
            }
        };
        thread.start();
        thread.join();
        System.out.println("------>");
        System.out.println(testThreadLocal.getLong());
        System.out.println(testThreadLocal.getString());
    }
}

 

ThreadLocal可能造成的内存泄露,如何解决,底层使用了哪种引用方式?

1、实现多个线程之间的资源互相隔离,达到安全并发的目的。

2、ThreadLocal主要的是用于独享自己的变量,避免一些资源的争夺,从而实现了空间换时间的思想。而synchronised则主要用于临界(冲突)资源的分配,从而能够实现线程间信息同步,公共资源共享等

3、变量既然成为了每个线程内部的局部变量,自然就不会存在并发问题了;

 

造成内存泄露的原因

1、ThreadLocal中用到的自己定义的Map(和常用的Map接口不同)中

2、使用的Key值是一个WeakReference类型的值(弱引用会在下一次GC时马上释放而不管是否被引用)。

3、那么如果这个Key在GC时被释放了,就会导致Value永远都不会被调用到,但是如果线程不结束,又一直存在。

 

ThreadLocalMap是ThreadLocal自己实现的一个Map,而这个Map用使用了ThreadLocal作为了一个弱引用的Key。

 

其实每个Thread里面都有一个Map,Map里面的Key是ThreadLocal类的一个实例,之所以会比较混淆主要还是因为这里的Map又是ThreadLocal里面的一个内部静态类。

 

为什么key使用弱引用?(key是当前ThreadLocal的实例)

ThreadLocal@123456对象只是作为ThreadLocalMap的一个key而存在的,现在它被回收了,但是它对应的value并没有被回收,内存泄露依然存在!而且key被删了之后,变成了null,value更是无法被访问到了!针对这一问题,ThreadLocalMap类的设计本身已经有了这一问题的解决方案,那就是在每次get()/set()/remove()ThreadLocalMap中的值的时候,会自动清理key为null的value。如此一来,value也能被回收了。

 

内存泄露(本该回收的无用对象没有得到回收)

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值