ThreadLocal的使用与理解

用过EventBus的同学都知道,在接收发出事件时,会有四种线程模式。那他是内部是怎样实现的呢?近日忙里偷闲,简单看了下EventBus源码,发现其发送事件前检查发送事件的线程状态(源码标注处),然后根据postingState去做一些线程切换的操作。

扯了半天发现跟标题ThreadLocal没半毛钱关系,哈哈哈。显然不可能,currentPostingThreadState其实就是一个ThreadLocal。终于步入正题了,本文将结合源码对ThreadLocal的使用及原理进行简单剖析,大致分为以下3个模块:

一、ThreadLocal是什么

二、ThreadLocal怎么用

三、源码分析

以下如果有不恰当或者错误的地方,还希望各位大佬指点!

一、ThreadLocal是什么

ThreadLocal,顾名思义,线程本地变量,也有叫线程本地存储的,差不多意思。在日常开发中,我们经常会遇到多线程的场景,比如数据库连接操作,都需要对connection进行持有。在这类场景下,出于线程安全的考虑,一般有两种做法:
1、方法加锁,同步操作。线程排队,依次执行。这种方法一定程度上影响程序执行效率,因为一个线程在使用共享变量时,其他线程只有等待。(时间换空间)
2、ThreadLocal实现。ThreadLocal在每个线程中将共享变量会创建一个副本,所有操作都是基于线程自己的副本变量,不影响其他线程,既能解决线程安全问题,又不会影响程序执行性能。缺点是创建副本时,对资源消耗增加(空间换时间)

 

二、ThreadLocal怎么用

Talk is cheap,show your code

1、构建一个模拟数据库连接的Connection实体,两个属性,id为整型,name为String型

public class Connection {

    private int id;   //数据库连接id
    private String name;   //数据库连接名称

    public Connection(int id,String name){
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Connection{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

2、数据库连接管理类,使用单例模式,一个get()、set()方法,实际内部就是将数据从ThreadLocal获取和存储。

public class ConnectionManager {

    private static ConnectionManager sInstance;

    //使用ThreadLocal存储Connection
    private ThreadLocal<Connection> connection = new ThreadLocal<>();

    private ConnectionManager(){}

    public static ConnectionManager getInstance(){
        if (sInstance==null){
            synchronized (ConnectionManager.class){
                if (sInstance == null) {
                    sInstance = new ConnectionManager();
                }
            }
        }
        return sInstance;
    }

    public Connection getConnection() {
        return connection.get();
    }

    public void setConnection(Connection connection) {
        this.connection.set(connection);
    }

}

3、准备工作完毕,写个main方法测试一下。这里其实是模拟三个线程操作数据库,一个主线程,两个子线程,执行完毕,将其连接的属性打印出来。

public class TestThreadLocal {

    public static void main(String[] args) throws InterruptedException {
        
        //在主线程创建连接
        ConnectionManager.getInstance().setConnection(new Connection(1,"主线程连接"));
        System.out.println(ConnectionManager.getInstance().getConnection().toString());

        //在子线程1创建连接
        Thread thread1 = new Thread(){
            @Override
            public void run() {
                super.run();
                ConnectionManager.getInstance().setConnection(new Connection(2,"thread1连接"));
                System.out.println(ConnectionManager.getInstance().getConnection().toString());
            }
        };
        thread1.start();
        //堵塞主线程
        thread1.join();

        //在子线程1创建连接
        Thread thread2 = new Thread(){
            @Override
            public void run() {
                super.run();
                ConnectionManager.getInstance().setConnection(new Connection(3,"thread2连接"));
                System.out.println(ConnectionManager.getInstance().getConnection().toString());
            }
        };
        thread2.start();
        //堵塞主线程
        thread2.join();

        System.out.println(ConnectionManager.getInstance().getConnection().toString());

    }
}

执行完毕,打印log如下。显然可以看出各个线程互不影响,达到了线程安全的目的。

当然这里有个注意点,在使用ThreadLcoal的get方法前必须进行set,否则会报空指针异常。是不是感觉不够优雅,其实ThreadLocal还提供了initialValue()方法,重写该方法,初始化数据,也可以达到set的效果。这里就不再赘述。

三、源码分析

大致思路:ThreadLocal能够实现了线程之前互不影响,资源独立,其实归根究底是ThreadLocalMap(ThreadLocal的内部类)的功劳。是因为每个Thread有一个类型为ThreadLocal.ThreadLocalMap的实例变量threadLocals,也就是说每个线程有一个自己的ThreadLocalMap,key是ThreadLocal包装成的一个弱引用,value为代码中实际放入的各种类型的值。当线程set或者get值的时候,都会在自己的ThreadLocalMap里操作。

关于ThreadLocal内部详细实现,请参见https://www.cnblogs.com/micrari/p/6790229.html

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值