一:主要涉及到的源码如下set()方法
主要是复制当前线程变量生成一个线程变量副本,需要被存储的值放置在本地变量的副本中
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
获取ThreadLocalMap对象,该对象也是作为当前线程(Thread.currentThread())对象的一个成员变量
如果查到当前线程已经创建了ThreadLocalMap对象,就直接从当前线程中获取该成员变量
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
如果没有获取到,则通过当前线程以及要第一次添加的值创建一个ThreadLocalMap对象
/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
二 get()方法源码如下:
直接从当前编程变量的副本中获取存储的值,如果当前线程没有相应存储的值,就直接设置初始化值 并返回
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
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();
}
三 ThreadLocal应用场景如下:数据库连接池的连接(Connection)管理
package com.dong.day;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;
import org.apache.tomcat.dbcp.dbcp.BasicDataSource;
/**
*
* 数据库连接池
* @author Administrator
* 用ThreadLocal管理Connection连接
*/
public class Dbutils {
private static BasicDataSource source;
private static ThreadLocal<Connection> local;//本地线程保存connection局部变量
static{
try {
//加载配置文件
Properties pro =new Properties();
//获取读取流
InputStream in=Dbutils.class.getClassLoader().getResourceAsStream("jdbc.properties");
//从配置文件中读取数据
pro.load(in);
//将读取流关闭
in.close();
//初始化连接池
source=new BasicDataSource();
//设置驱动
source.setDriverClassName(pro.getProperty("driverClassName"));
source.setUrl(pro.getProperty("url"));//url路径
source.setUsername(pro.getProperty("username"));//账号
source.setPassword(pro.getProperty("password"));//密码
source.setInitialSize(Integer.valueOf(pro.getProperty("initsize")));//初始化连接数量
source.setMaxActive(Integer.valueOf(pro.getProperty("maxactive")));//最大连接数
source.setMaxIdle(Integer.valueOf(pro.getProperty("minidle")));//最小空闲数
source.setMaxWait(Long.valueOf(pro.getProperty("maxwait")));//最大等待时间
//初始化线程本地
local =new ThreadLocal<Connection>();
} catch (IOException e) {//这个是io流方面的异常
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e){ //捕获更大范围的异常
e.printStackTrace();
}
}
/**
* 定义一个获取Connection连接的方法
*
*/
public static Connection getConnection(){
Connection conn =null;
try {
conn =source.getConnection();
local.set(source.getConnection());
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
/**
* 定义一个关闭Connection连接的方法
*
*/
public static void closeConnection(){
Connection conn = local.get();//获取连接池
if(conn!=null){
try {
conn.setAutoCommit(true);//设置为自动提交事务
conn.close();//连接进行关闭,connection对象并不是真正意义上的消除,而是归还到线程池中
//由于该连接已经归还到线程池中,线程本地保存也就没有意义,线程本地没有必要保存改变量
local.remove();//移除 手动的移除 可以放置内存泄漏 因为thread和ThreadLocalMap的生命周期一样长,会造成ThreadLocalMap不能及时释放
//内存泄漏概念如下:
/**
* 内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,
* 造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果.
*/
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception {
System.out.println(Dbutils.getConnection().getClientInfo());
Connection conn=Dbutils.getConnection();
String sql="SELECT * FROM LOG";
PreparedStatement ps = conn.prepareStatement(sql);
boolean s = ps.execute();
System.out.println(s);//执行成功
}
}
相应测试案例如下:
package com.dong.day;
import java.util.HashMap;
import java.util.Map;
public class ThreadLocalDemo {
/***
* ThreadLocal提供了线程的局部变量,每个线程都可以通过set()和get()来对这个局部变量进行操作,
* 但不会和其他线程的局部变量进行冲突,实现了线程的数据隔离~。
* @param args
* @return
* @since 20180614 学习ThreadLocal
*
* 首先ThreadLocal和本地线程没有一毛钱关系,更不是一个特殊的Thread,它只是一个线程的局部变量(其实就是一个Map),
* ThreadLocal会为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,
* 而不会影响其它线程所对应的副本。这样做其实就是以空间换时间的方式(与synchronized相反),
* 以耗费内存为代价,单大大减少了线程同步(如synchronized)所带来性能消耗以及减少了线程并发控制的复杂度。
*
* 如果想要在ThreadLocal中放置多个变量值的时候,可以将将多个变量封装一个对象,进行放置即可
*
* Hibernate对线程池的管理也采用了ThreadLocal进行管理
*
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadLocal<String> th=new ThreadLocal<String>();
th.set("sss");
System.out.println(th.get());
ThreadLocal<Map<String,Object>> th1=new ThreadLocal<Map<String,Object>>();
Map<String,Object> map=new HashMap<String,Object>();
map.put("key", "value");
th1.set(map);
System.out.println(th1.get().get("key"));
}
}
运行结果如下:
sss
value
后续再有深化,再持续更新!