ThreadLocal

ThreadLocal是什么

ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多。可能很多朋友都知道ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
通常我们在使用jdbc做数据库连接时是需要手动控制事物的,这个时候就要保证数据库连接connection在同一个线程中的唯一性,而connection又不是单例的这个时候我们就要借助于ThreadLocal

ThreadLocal的用法

首先创建一个ThreadLocal对象,在获取值的时候调用get方法,判断我们拿到的值是否为null如果为null就创建一个要获取的对象,并且调用ThreadLocal的set方法将值设置进去。当以后我们在通过ThreadLocal的get方法获取值的时候,同一个线程中就是唯一的了

这是一个获取数据库连接的代码

private static ThreadLocal<Connection> tol = new ThreadLocal<Connection>();
//获取Connection连接
public static Connection getConnection() throws Exception{
	//获取连接
	Connection conn = tol.get();
	if(conn==null){ 
		//如果为空就创建一个
		//加载驱动
		Class.forName(prop.getProperty("driver"));
		//创建连接
		conn = DriverManager.getConnection(prop.getProperty("url"),
				prop.getProperty("username"), prop.getProperty("password"));
		//设置进入ThreadLocal
		tol.set(conn); 
	}
	//返回Connection
	return conn;
}

ThreadLocal的工作原理

首先ThreadLocal是放在线程的ThreadLocalMap这个类型的成员变量中的,这个map是将ThreadLocal作为键,任意类型作为值,下面就是ThreadLocalMap的源码

 static class Entry extends WeakReference<ThreadLocal<?>> {
	/** 与此ThreadLocal关联的值. */
	Object value;
	Entry(ThreadLocal<?> k, Object v) {
	   super(k);
	   value = v;
	}
}

每次调用ThreadLocal.get()方法时,会首先获取当前线程对应的ThreadLocalMap,然后以当前ThreadLocal做键获取值,当没有取到时会调用setInitialValue方法。

 public T get() {
  Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

setInitialValue方法内部首先调用initialValue方法返回一个null,然后在获取当前线程,获取当前线程的ThreadLocalMap,如果当前线程存在就把initialValue方法的返回值放到map中,如果不存在就为当前线程创建一个ThreadLocalMap并且把initialValue方法的返回值放到map中。

private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

所以我们可以通过重写initialValue方法来使我们获取不到值得时候,直接在initialValue方法中为ThreadLocal设置值,下面就一个通过重写initialValue来获取值的例子

public class TestInitialValue {
   static ThreadLocal<Long> idThreadLocal = new ThreadLocal(){
       @Override
       protected Long initialValue() {
          System.out.println("==============");
          return Thread.currentThread().getId();
   	  }
   };
   
    public static void main(String[] args) {
        Long mainId = idThreadLocal.get();
        System.out.println("主线程id" + mainId);
        Long mainId1 = idThreadLocal.get();
        System.out.println("主线程id" + mainId1);
    }
}
==============
主线程id1
主线程id1

可以看到当一次获取的时候是无法获取到值得,所以调用了initialValue方法,由于重写了initialValue方法,所以第二次是可以直接拿到值的,就没有调用initialValue方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值