java多线程之ThreadLocal的使用

一、什么是ThreadLocal?

多线程之间的通信可以通过共享变量来实现(通常以 public static 来修饰共享变量),当有多个线程对共享变量进行操作时,为保证其安全性,我们通常需要对其进行同步处理来保证安全性。但是这又会造成程序执行效率的降低。
在某些情况下,若我们是对共享变量的副本进行操作,而非直接操作其本体,那么就可以在既保证效率的情况下又保证其安全性(如数据库连接池获取connection,以及getsession等场景),这个时候我们的主角ThreadLocal就出现了。
ThreadLocal,线程本地变量。ThreadLocal为共享变量在每个线程中都创建了一个副本,每个线程都可以访问自己内部的副本变量。各个线程之间的变量互不干扰。

二、ThreadLocal的基本操作

方法名作用
public T get() { }获取当前线程中共享变量的变量副本
public void set(T value) { }设置当前线程中共享变量的变量副本
public void remove() { }移除当前线程中共享变量的变量副本
protected T initialValue() { }为当前线程中共享变量的变量副本进行初始化

实例测试

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();  // 这里join方法的作用就是: 主线程需要等待子线程执行完成之后再执行
         
        System.out.println(test.getLong());
        System.out.println(test.getString());
    }
}

/*
	get前若想不set且能正常访问的话,则必须重写initeValue()方法,重写部分如下:
	
    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();
        };
    };
    
*/

测试结果:
在这里插入图片描述

结论:
1.在多线程执行过程中,main线程和thread1线程执行时分别保存了共享变量的副本值,且两线程互不干扰。
(第二次打印thread1的值是为了证明二者保存的副本值确实是不同的)
2.每个线程中可以有多个threadlocal变量
3.在进行get之前,必须进行set否则会报空指针异常。若想不set直接get且能正常访问的话,则必须重写initeValue()方法。

三、ThreadLocal实际应用场景

3.1 数据库连接场景

不使用threadlocal情况(不考虑多线程):

class ConnectionManager {
     
    private static Connection connect = null;
     
    public static Connection openConnection() {
        if(connect == null){
            connect = DriverManager.getConnection();
        }
        return connect;
    }
     
    public static void closeConnection() {
        if(connect!=null)
            connect.close();
    }
}

使用threadlocal解决:

private static ThreadLocal<Connection> connectionHolder= new ThreadLocal<Connection>() {
	public Connection initialValue() {
	    return DriverManager.getConnection(DB_URL);
	}
};
 
public static Connection getConnection() {
	return connectionHolder.get();
}

3.2 Session管理

private static final ThreadLocal threadSession = new ThreadLocal();
 
public static Session getSession() throws InfrastructureException {
    Session s = (Session) threadSession.get();
    try {
        if (s == null) {
            s = getSessionFactory().openSession();
            threadSession.set(s);
        }
    } catch (HibernateException ex) {
        throw new InfrastructureException(ex);
    }
    return s;
}

参考来源:

本文没有对threadlocal底层源码进行深入解析,只是在理解与应用上做了简单阐述。详细内容参考大神博文

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜鹊先生Richard

随缘~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值