ThreadLocal原理详解——原来ThreadLocal是这和强大的存在

ThreadLocal

定义

为每一个线程提供变量的副本,实现了线程的隔离,也可以保证线程的安全性。

和ThreadLocal相关类

  • ThreadLocal内部类ThreadLocalMap
  • ThreadLocalMap.Entry ThreadLocalMap内部类
  • Thread类

ThreadLocal是一个带泛型的类

public class ThreadLocal<T> {}

源码分析

Android中最典型用到ThreadLocal地方在Looper里

public final class Looper {
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    public static void loop() {
		final Looper me = myLooper();
	}
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
}

sThreadLocal.get();源码解析

//泛型T为Looper,我们都会把T 替换成Looper
public class ThreadLocal<Looper> {
	public Looper get() {
	    Thread t = Thread.currentThread();
	    //第一步: 通过getMap(t)获取一个ThreadLocalMap
	    ThreadLocalMap map = getMap(t);
	    //第一次进来这个map肯定为null
	    if (map != null) {
	    	//ThreadLocalMap.getEntry,获取缓存
	        ThreadLocalMap.Entry e = map.getEntry(this);
	        if (e != null) {
	            @SuppressWarnings("unchecked")
	            Looper result = (Looper)e.value;
	            return result;
	        }
	    }
	    //第三步 创建一个Looper
	    return setInitialValue();
	}
	
	ThreadLocalMap getMap(Thread t) {
		//第二步: 返回的是当前线程里的一个成员变量ThreadLocalMap threadLocals
	    return t.threadLocals;
	}
	
    private Looper setInitialValue() {
    	//第四步:通过initialValue()api得到了一个Looper 而initialValue()方法直接return 了一个null
    	//所这在没有做调用其它api的情况下ThreadLocal.get()方法获取的值为null
        Looper value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
        	//第五步: 创建ThreadLocalMap ,将Thread 里的成员变量threadLocals 附值
        	//threadLocals  = new ThreadLocalMap(this, firstValue);this为ThreadLoca, firstValue第一次为null
        	//这个时候当前线程的threadLocals就不为null了
            createMap(t, value);
        return value;
    }
    
    protected Looper initialValue() {
        return null;
    }
    
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
}

class Thread implements Runnable {
    ThreadLocal.ThreadLocalMap threadLocals = null;
}

//再来分析Looper里是怎么将sThreadLocal里设置值的

public final class Looper {
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //第六步: 调用sThreadLocal.set方法将新创建的Looper给设置进去
        sThreadLocal.set(new Looper(quitAllowed));
    }
	
	//第九步:当set完值的时候再去get这就完全不一样了。
	public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    
	public Looper get() {
	    Thread t = Thread.currentThread();
	    //以当前线程为key去找值,获取的是当前线程的成员变量threadLocals,这个时候也不为null。且是每个Thread类独有的
	    ThreadLocalMap map = getMap(t);
		//这个时候map不为null了,已经有值了
	    if (map != null) {
	   		//第十步:当set完值的时候再去get这就完全不一样了。
	    	//ThreadLocalMap.getEntry,获取缓存 ThreadLocalMap其实相当于一个这样的HashMap<Thread,Looper>
	    	//ThreadLocalMap.Entry 是一个WeakReference<ThreadLocal<Looepr>>
	    	//ThreadLocalMap里有个成功变量Entry[] table他会保存很多Entry 
	    	//每个ThreadLocalMap.Entry又执有了不同的ThreadLocal 和泛型T(Looper)类
	        ThreadLocalMap.Entry e = map.getEntry(this);
	        if (e != null) {
	            @SuppressWarnings("unchecked")
	            Looper result = (Looper)e.value;
	            return result;
	        }
	    }
	    //第三步 创建一个Looper
	    return setInitialValue();
	}
}

public class ThreadLocal<Looper> {
    public void set(Looper value) {
        Thread t = Thread.currentThread();
        //第七步: 通过getMap获取当前线程的ThreadLocalMap ,如果在set之前什么没有调用ThreadLocal里的get方法那么这个map就为null
        //如果在set之前调用了get方法,那么这个map就不为null
        ThreadLocalMap map = getMap(t);
        if (map != null)
       		//第八步: 将传进来的Looper给设置进来了,key为ThreadLocal也主是Looper里的成功变量sThreadLocal  valus为创建的Looper
            map.set(this, value);
        else
        	//第五步: 创建ThreadLocalMap ,将Thread 里的成员变量threadLocals 附值
        	//threadLocals  = new ThreadLocalMap(this, firstValue);this为ThreadLocal, firstValue这个时候为value 不为null了
        	//这个时候当前线程的threadLocals就不为null了
            createMap(t, value);
    }
}

总结

1:每一个Thread都执有一个ThreadLocalMap变量threadLocals 他是通过ThreadLocal的createMap()方法创建

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

2:ThreadLocalMap是ThreadLocal的一个内部类,他相当于一个HashMap<ThreadLocal,Object>,以ThreadLocal为Key,所以这里可以保证每一个ThreadLocal只能有一个值
3:而每一个Thread都执有一个ThreadLocalMap变量所以每一个线程里只要传入的ThreadLocal不变获取到的值也是唯一的。
4:ThreadLocalMap里有一个内部类 Entry
5:Entry这个类他会执行一个ThreadLocal和传进来的Object
6:ThreadLoca.get()先是获取本线程的一个ThreadLocalMap,然后通过ThreadLocalMap的getEntry方法找到值

Threadlocal类提供set和get方法,
每一个Thread内执有一个ThreadLocalMap成员变量
ThreadLocalMap有一个Entry[] table成员变量

Threadlocal的get方法
如果之前没有调用过set或者get那么会通过setInitialValue方法为当前Thread里的成员变量threadLocals(ThreadLocalMap)赋值,初始化ThreadLocalMap时,会为其内部成功变量table初始化,并将传入的Threadlocal当作key,null当作value当作第一个数组元素

如果之前调用过set或者get 那么threadLocals(ThreadLocalMap)不为null,会通过threadLocals的getEntry方法获取到传入的Threadlocal对应的Entry值,再获取Entry的value就拿到了想要的

Threadlocal的set方法
如果之前没有调用过set或者get则会通过createMap方法为当前Thread的成员变量threadLocals(ThreadLocalMap)赋值,初始化ThreadLocalMap时,会为其内部成功变量table初始化,并将传入的Threadlocal当作key,传入的value当作第一个数组元素

如果之前调用过set或者get 那么当前Thread的threadLocals(ThreadLocalMap)有值,则直接调用ThreadLocalMap的set方法赋值
ThreadLocalMap的set方法则会寻找传过来的Threadlocal并找到对应的值再替换

示例

public class ThreadLocalTest {
	
	public static void main (String [] args) {

   		 ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
    		    @Override
    		    protected Integer initialValue() {
      	     	 	return 1;
     		   }
 		   };
        new Thread(() -> {
            Integer integer = threadLocal.get();
            threadLocal.set(integer + 4);
            System.out.println("threadLocal1111: " + threadLocal.get());
        }).start();
        
        new Thread(() -> {
            System.out.println("threadLocal22222: " + threadLocal.get());
        }).start();

        new Thread(() -> {
        	System.out.println("threadLocal33333 before: " + threadLocal.get());
            threadLocal.set(9);
            System.out.println("threadLocal33333 after :" + threadLocal.get());
        }).start();
	}
	日志输出如下:
	System.out: threadLocal1111: 5
	System.out: threadLocal22222: 1
	System.out: threadLocal33333 before: 1
	System.out: threadLocal33333 after :9
	每一个线程 threadLocal都是不同的,他们也是隔离的,在一个线程设置值对其它线程是没有影响的
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值