Android技术---ThreadLocal详解

原创 2018年04月16日 18:36:30

前言

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

情景

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

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

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对象并不是一个了。
用语言来讲的话可能还是不太形象,画个图

然后我们用代码来实现


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

C++中的文件输入/输出(5):二进制文件的处理

C++中的文件输入/输出(5)原作:Ilia Yordanov,  loobian@cpp-home.com 二进制文件的处理 虽然有规则格式(formatted)的文本(到目前为止我所讨论的所有文件...
  • Kusk
  • Kusk
  • 2003-08-24 15:33:00
  • 8230

android之ThreadLocal详解

threadlocal并不是线程,记得最早几年前面试android的时候,被人问到threadlocal当时没听过,直接回答说是线程,现在想想当时真怂。threadlocal而是一个线程内部的存储类,...
  • eandroidhu
  • eandroidhu
  • 2016-03-17 10:48:44
  • 1479

Android的消息机制之ThreadLocal的工作原理

提到消息机制大家应该都不陌生,在日常开发中不可避免地要涉及到这方面的内容。从开发的角度来说,Handler是Android消息机制的上层接口,这使得开发过程中只需要和Handler交互即可。Handl...
  • singwhatiwanna
  • singwhatiwanna
  • 2015-09-10 21:54:18
  • 26186

ThreadLocal类详解与源码分析

概述我们知道Spring通过各种DAO模板类降低了开发者使用各种数据持久技术的难度。这些模板类都是线程安全的,也就是说,多个DAO可以复用同一个模板实例而不会发生冲突。 我们使用模板类访问底层数据,根...
  • shenlei19911210
  • shenlei19911210
  • 2015-11-26 19:48:54
  • 2710

Java ThreadLocal使用浅析

  Java ThreadLocal使用浅析   JAVA API文档里关于ThreadLocal的定义是:Thisclass provides thread-local variables. The...
  • abing37
  • abing37
  • 2009-08-18 20:14:00
  • 16086

java多线程模式ThreadLocal原理简述及其使用详解

java多线程模式ThreadLocal原理简述及其使用详解,代码下载地址:http://www.zuidaima.com/share/1781557457128448.htm...
  • yaerfeng
  • yaerfeng
  • 2014-05-18 11:51:12
  • 5837

Android开发——ThreadLocal功能介绍

ThreadLocal适用于某些数据以线程为作用域并且不同线程具有不同数据副本的场景。 比如对于Handler来说,它需要获取当前线程的Looper,很显然Looper的作用域就是线程并且不同线程具有...
  • SEU_Calvin
  • SEU_Calvin
  • 2016-08-04 16:07:58
  • 11310

ThreadLocal使用及原理解析

1.ThreadLocal干什么的?       我们知道,在多线程程序中,同一个线程在某个时间段只能处理一个任务.我们希望在这个时间段内,任务的某些变量能够和处理它的线程进行绑定,在任务需要使...
  • u011516800
  • u011516800
  • 2015-01-23 15:07:13
  • 1949

大白话讲解ThreadLocal的原理

ThreadLocal顾名思义,本地线程,可以理解为本地线程变量,说白了就是操作本地线程的局部变量。 下面我们通过源码进行说明: 首先,我们看一下ThreadLocal的set方法源码实现: p...
  • qq_30698633
  • qq_30698633
  • 2017-08-12 17:10:36
  • 111

ThreadLocal的原理和在框架中的应用

我们知道Spring通过各种DAO模板类降低了开发者使用各种数据持久技术的难度。这些模板类都是线程安全的,也就是说,多个DAO可以复用同一个模板实例而不会发生冲突。       我们使用模板类访问底...
  • u010796790
  • u010796790
  • 2016-08-13 17:31:35
  • 289
收藏助手
不良信息举报
您举报文章:Android技术---ThreadLocal详解
举报原因:
原因补充:

(最多只允许输入30个字)