美文网首页
Android技术---ThreadLocal详解

Android技术---ThreadLocal详解

作者: MartinHan01 | 来源:发表于2018-12-19 00:56 被阅读0次

    前言

    不管是平时开发,或者是阅读别人的代码关于多线程的时候。我们总会遇到这个ThreadLocal。
    今天算是偶尔也和大家一起来说说Java基础的东西。
    ThreadLocal从字面的意思来说其实就是一个线程局部变量,

    情景

    我们假想一个情景,有3个线程,A线程和B线程,还有我们的主线程。
    有一个数字的对象在主线程里,然后A线程和B线程一起读取做一些操作

    先画个图解释一下,再上代码

    <img src="http://martinhan.site/images/2018-04-16_171655.png" width="479" height="518">

    package com.martinhan.zeroone;
    
    public class Num {
        private int value;
        
        public Num() {
            super();
        }
    
        public Num(int value) {
            super();
            this.value = value;
        }
    
        public int getValue() {
            return value;
        }
    
        public void setValue(int value) {
            this.value = value;
        }
        
        public void addValue(int value) {
            this.value += value;
        }
    }
    
    
    package com.martinhan.zeroone;
    
    public class ThreadLocalTest1 {
        
        
        public static void main(String[] args) {
            Num num = new Num(100);
            Thread aThread = new Thread(){
                
                @Override
                public void run() {
                    try {
                        Thread.sleep(100L);
                        num.addValue(200);
                        System.out.println("thread ---" + Thread.currentThread() + " value is " + num.getValue());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    };
                }
            };
            Thread bThread = new Thread(){
                
                @Override
                public void run() {
                    try {
                        Thread.sleep(100L);
                        num.addValue(200);
                        System.out.println("thread ---" + Thread.currentThread() + " value is " + num.getValue());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    };
                }
            };
            aThread.start();
            bThread.start();
        }
    }
    
    
    

    执行结果如下

    thread ---Thread[Thread-0,5,main] value is 300
    thread ---Thread[Thread-1,5,main] value is 300
    

    其实正常情况下,我们当然不希望是这种结果了。
    A线程希望改完了之后,值是300
    B线程希望改完了之后值才是500

    ThreadLocal引入

    此时此刻,我们就想,可不可以这样呢,我们自己做一个HashMap,key放线程的id,然后value放各个线程需要的值。
    有了这个机制,我们就可以实现各个线程其实对应的实际Num对象并不是一个了。
    用语言来讲的话可能还是不太形象,画个图

    <img src="http://martinhan.site/images/2018-04-16_173909.png" width="481" height="482">

    然后我们用代码来实现

    
    package com.martinhan.zeroone;
    
    import java.util.HashMap;
    
    public class Num2 {
        private HashMap<Long,Integer> map = new HashMap<Long,Integer>();
        
        public Num2() {
            super();
        }
    
        public Num2(int value) {
            super();
            this.map.put(Thread.currentThread().getId(), value);
        }
    
        public Integer getValue() {
            return map.get(Thread.currentThread().getId());
        }
    
        public synchronized void setValue(int value) {
            this.map.put(Thread.currentThread().getId(), value);
        }
        
        public synchronized void addValue(int value) {
            if(map.containsKey(Thread.currentThread().getId()) == false) {
                return ;
            }
            int oldvalue = map.get(Thread.currentThread().getId());
            map.put(Thread.currentThread().getId(), oldvalue + value);
        }
    }
    
    
    package com.martinhan.zeroone;
    
    public class ThreadLocalTest2 {
        
        public static void main(String[] args) {
            Num2 num = new Num2();
            Thread aThread = new Thread(){
                
                @Override
                public void run() {
                    try {
                        Thread.sleep(100L);
                        num.setValue(100);
                        num.addValue(200);
                        System.out.println("thread ---" + Thread.currentThread() + " value is " + num.getValue());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    };
                }
            };
            Thread bThread = new Thread(){
                
                @Override
                public void run() {
                    try {
                        Thread.sleep(100L);
                        num.setValue(100);
                        num.addValue(400);
                        System.out.println("thread ---" + Thread.currentThread() + " value is " + num.getValue());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    };
                }
            };
            aThread.start();
            bThread.start();
        }
    }
    
    
    

    执行结果如下

    thread ---Thread[Thread-1,5,main] value is 500
    thread ---Thread[Thread-0,5,main] value is 300
    

    setValue,addValue方法做了线程同步,因为HashMap本身就是不安全的。这次的执行结果就对了。
    按照了我们的图示,这次每个线程都操作自己的数据。然后不会有线程不安全的情况了。
    可能有人说,加同步不就好了么,其实不是想为大家一起说下ThreadLocal嘛,举个例子

    ThreadLocal

    ThreadLocal其实做的事情就大概如此了,只是大概如此,如果需要深究细节,需要去仔细看源码。
    以上的代码,还有几处不完善,那个HashMap里的对象会一直存在,他并不会随着线程的结束而删除。

    在Java源码里,真正的map是在Thread里面的,

    先举个例子

    package com.martinhan.zeroone;
    
    public class ThreadLocalTest3 {
    
        private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
        static int value = 100;
    
        public static void main(String[] args) throws InterruptedException {
            Thread aThread = new Thread(){
                @Override
                public void run() {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    threadLocal.set(value + 200);
                    System.out.println("thread ---" + Thread.currentThread() + " value is " + threadLocal.get());
                }
            };
    
            Thread bThread = new Thread(){
                @Override
                public void run() {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    threadLocal.set(value + 400);
                    System.out.println("thread ---" + Thread.currentThread() + " value is " + threadLocal.get());
                }
            };
            aThread.start();
            bThread.start();
            
        }
    }
    

    然后我们从threadLocal.set方法来读,实现如下

    public void set(T value) {
            //获取当前线程
            Thread t = Thread.currentThread();
            //获取t线程对象的成员变量threadLocals,threadLocals是一个ThreadLocalMap
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                //最后如果map不为空的话,就放入
                map.set(this, value);
            }
            else {
                createMap(t, value);
            }
        }
    
    ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
    

    在线程退出的时候有如下代码

     private void exit() {
            if (group != null) {
                group.threadTerminated(this);
                group = null;
            }
            /* Aggressively null out all reference fields: see bug 4006245 */
            target = null;
            /* Speed the release of some of these resources */
            threadLocals = null;
            inheritableThreadLocals = null;
            inheritedAccessControlContext = null;
            blocker = null;
            uncaughtExceptionHandler = null;
        }
    

    这里面手动置空了threadLocals成员变量

    源码地址

    源码链接

    写在最后

    ThreadLocal大体的思路如此,个人觉得懂了两个图,就算可以说大致懂了,然后读源码
    希望读者有问题,可以和我探讨,喜欢一起交流技术问题

    关于我

    个人博客:MartinHan的小站

    博客网站:hanhan12312的专栏

    知乎:MartinHan01

    相关文章

      网友评论

          本文标题:Android技术---ThreadLocal详解

          本文链接:https://www.haomeiwen.com/subject/lenihqtx.html