ThreadLocal--线程详解

概念

1、ThreadLocal是一种变量类型,我们称之为“线程局部变量”。每个线程访问这种变量的时候都会创建该变量的副本,这个变量副本为线程私有;ThreadLocal类型的变量一般用private static加以修饰。

2、ThreadLocal中的常用方法:

ThreadLocal.get: 用来获取ThreadLocal在当前线程中保存的变量副本;
ThreadLocal.initialValue: ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值;
ThreadLocal.set: 设置ThreadLocal中当前线程共享变量的值;
ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。

1)get方法

public class ThreadLocal<T> {

   public T get() {
   
        Thread var1 = Thread.currentThread();   //取得当前线程
        ThreadLocal.ThreadLocalMap var2 = this.getMap(var1);  
        if (var2 != null) {
            ThreadLocal.ThreadLocalMap.Entry var3 = var2.getEntry(this);   //获取到<key,value>键值对
            if (var3 != null) {
                Object var4 = var3.value;
                return var4;   //如果获取成功,则返回value值
            }
        }
        return this.setInitialValue();   //如果map为空,则调用setInitialValue方法返回value
    }  

}

通过getMap方法获取当前线程中的所有ThreadLocal< T >记录threadLocals;先判断是否已有值,如果没有则调用setInitialvalue()赋值

ThreadLocalMap是ThreadLocal的内部类:

public class ThreadLocal<T> {
   
    static class ThreadLocalMap {
        
    }
}

getMap方法:调用当期线程var1,返回当前线程var1中的一个成员变量threadLocals

ThreadLocal.ThreadLocalMap getMap(Thread var1) {
        return var1.threadLocals;
    }

然后再查看Thread.threadLocals的值(在Thread类中):

public class Thread implements Runnable {
    ThreadLocalMap threadLocals = null;
}

发现threadLocals就是一个ThreadLocalMap;


2)setInitialvalue方法

public class ThreadLocal<T> {

   private T setInitialValue() {
        Object var1 = this.initialValue();
        Thread var2 = Thread.currentThread();
        ThreadLocal.ThreadLocalMap var3 = this.getMap(var2);
        if (var3 != null) {   //如果map不为空,就设置键值对
            var3.set(this, var1);
        } else {
            this.createMap(var2, var1);  //为空,再创建Map
        }

        return var1;
    }

}

===>查看createMap方法:

void createMap(Thread var1, T var2) {
        var1.threadLocals = new ThreadLocal.ThreadLocalMap(this, var2);
    }

====》ThreadLocal创建副本完成。

     每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本;

     初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。

     然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。


3)set方法

public class ThreadLocal<T> {

   public void set(T var1) {
        Thread var2 = Thread.currentThread();
        ThreadLocal.ThreadLocalMap var3 = this.getMap(var2);
        if (var3 != null) {
            var3.set(this, var1);
        } else {
            this.createMap(var2, var1);
        }

    }

}

先判断是否有值了,如果有则覆盖,没有则创建新的Map对象。

4)remove方法

public class ThreadLocal<T> {

   public void set(T var1) {
        Thread var2 = Thread.currentThread();
        ThreadLocal.ThreadLocalMap var3 = this.getMap(var2);
        if (var3 != null) {
            var3.set(this, var1);
        } else {
            this.createMap(var2, var1);
        }

    }

}

Thread通过ThreadLocalMap来存储线程中的变量,第一次获取时需要先通过set()将值保存进map中。通过泛型来区分给个变量的key。


示例:

public class Test {
    ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
    ThreadLocal<String> stringLocal = new ThreadLocal<String>();
 
     
    public void set() {
        longLocal.set(Thread.currentThread().getId());
        stringLocal.set(Thread.currentThread().getName());
    }
     
    public long getLong() {
        return longLocal.get();
    }
     
    public String getString() {
        return stringLocal.get();
    }
     
    public static void main(String[] args) throws InterruptedException {
        final Test test = new Test();
         
         
        test.set();
        System.out.println(test.getLong());
        System.out.println(test.getString());
     
         
        Thread thread1 = new Thread(){
            public void run() {
                test.set();
                System.out.println(test.getLong());
                System.out.println(test.getString());
            };
        };
        thread1.start();
        thread1.join();
         
        System.out.println(test.getLong());
        System.out.println(test.getString());
    }
}

在这里插入图片描述
分析:
a)main线程中和thread1线程中,longLocal保存的副本值和stringLocal保存的副本值都不一样;
b)实际通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中的;
c)为何threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,因为每个线程中可有多个threadLocal变量,就像上面代码中的longLocal和stringLocal;
d)在进行get之前,必须先set,否则会报空指针异常;
e)如果想在get之前不需要调用set就能正常访问的话,必须重写initialValue()方法。

示例 - 不用先set而直接调用get:

public class Test {
    ThreadLocal<Long> longLocal = new ThreadLocal<Long>(){
        protected Long initialValue() {
            return Thread.currentThread().getId();
        };
    };
    ThreadLocal<String> stringLocal = new ThreadLocal<String>(){;
        protected String initialValue() {
            return Thread.currentThread().getName();
        };
    };
 
     
    public void set() {
        longLocal.set(Thread.currentThread().getId());
        stringLocal.set(Thread.currentThread().getName());
    }
     
    public long getLong() {
        return longLocal.get();
    }
     
    public String getString() {
        return stringLocal.get();
    }
     
    public static void main(String[] args) throws InterruptedException {
        final Test test = new Test();
 
        test.set();
        System.out.println(test.getLong());
        System.out.println(test.getString());
     
         
        Thread thread1 = new Thread(){
            public void run() {
                test.set();
                System.out.println(test.getLong());
                System.out.println(test.getString());
            };
        };
        thread1.start();
        thread1.join();
         
        System.out.println(test.getLong());
        System.out.println(test.getString());
    }
}

3、使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值