前言:
知其然知其所以然,所以先来说一下ThreadLocal是为了什么用途而应运而生的。
作用:
ThreadLocal实现了线程的数据隔离,提供了属于线程的独享局部变量,每个线程都可以通过set()和get()来对这个局部变量进行操作。
用法:
对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。
1、ThreadLocal.get:
获取ThreadLocal中当前线程共享变量的值。
2、ThreadLocal.set:
设置ThreadLocal中当前线程共享变量的值。
3、ThreadLocal.remove:
移除ThreadLocal中当前线程共享变量的值。
4、ThreadLocal.initialValue:
ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值。
典型应用场景:
1.管理Connection
数据库连接池的connection让我们交由ThreadLocal来进行管理。为什么交给它来管理呢??ThreadLocal能够实现当前线程的操作都是用同一个Connection,保证了事务!
public static Connection getConnection() throws SQLException {
//获取Connection对象
Connection connection = source.getConnection();
//把Connection放进ThreadLocal里面
local.set(connection);
//返回Connection对象
return connection; }
//关闭数据库连接
public static void closeConnection() {
//从线程中拿到Connection对象
Connection connection = local.get();
try { if (connection != null) {
//恢复连接为自动提交
connection.setAutoCommit(true);
//这里不是真的把连接关了,只是将该连接归还给连接池
connection.close();
//既然连接已经归还给连接池了,ThreadLocal保存的Connction对象也已经没用了
local.remove(); }
} catch (SQLException e) { e.printStackTrace(); } } }
实现的原理
Local.set();
public void set(T value) {
// 得到当前线程对象
Thread t = Thread.currentThread();
// 这里获取ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 如果map存在,则将当前线程对象t作为key,要存储的对象作为value存到map里面去
if (map != null) map.set(this, value); else createMap(t, value); }
ThreadLocalMap是ThreadLocal的一个内部类。用Entry类来进行存储
我们的值都是存储到这个Map上的,key是当前ThreadLocal对象!
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原理总结
- 每个Thread维护着一个ThreadLocalMap的引用
- ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储
- 调用ThreadLocal的set()方法时,实际上就是往ThreadLocalMap设置值,key是ThreadLocal对象,值是传递进来的对象
- 调用ThreadLocal的get()方法时,实际上就是往ThreadLocalMap获取值,key是ThreadLocal对象
ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。
对象关系引用图
内存泄露:
ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,想要避免内存泄露就要手动remove()掉!
代码示例
package threadLocal;
/**
* ThreadLocal用法
* @author coshaho
*
*/
public class MyThreadLocal
{
private static final ThreadLocal<Object> threadLocal = new ThreadLocal<Object>(){
/**
* ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值
*/
@Override
protected Object initialValue()
{
System.out.println("调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!");
return null;
}
};
public static void main(String[] args)
{
new Thread(new MyIntegerTask("IntegerTask1")).start();
new Thread(new MyStringTask("StringTask1")).start();
new Thread(new MyIntegerTask("IntegerTask2")).start();
new Thread(new MyStringTask("StringTask2")).start();
}
public static class MyIntegerTask implements Runnable
{
private String name;
MyIntegerTask(String name)
{
this.name = name;
}
@Override
public void run()
{
for(int i = 0; i < 5; i++)
{
// ThreadLocal.get方法获取线程变量
if(null == MyThreadLocal.threadLocal.get())
{
// ThreadLocal.set方法设置线程变量
MyThreadLocal.threadLocal.set(0);
System.out.println("线程" + name + ": 0");
}
else
{
int num = (Integer)MyThreadLocal.threadLocal.get();
MyThreadLocal.threadLocal.set(num + 1);
System.out.println("线程" + name + ": " + MyThreadLocal.threadLocal.get());
if(i == 3)
{
MyThreadLocal.threadLocal.remove();
}
}
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
public static class MyStringTask implements Runnable
{
private String name;
MyStringTask(String name)
{
this.name = name;
}
@Override
public void run()
{
for(int i = 0; i < 5; i++)
{
if(null == MyThreadLocal.threadLocal.get())
{
MyThreadLocal.threadLocal.set("a");
System.out.println("线程" + name + ": a");
}
else
{
String str = (String)MyThreadLocal.threadLocal.get();
MyThreadLocal.threadLocal.set(str + "a");
System.out.println("线程" + name + ": " + MyThreadLocal.threadLocal.get());
}
try
{
Thread.sleep(800);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
}
输出结果
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程IntegerTask1: 0
线程StringTask1: a
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程StringTask2: a
线程IntegerTask2: 0
线程StringTask1: aa
线程StringTask2: aa
线程IntegerTask2: 1
线程IntegerTask1: 1
线程StringTask1: aaa
线程StringTask2: aaa
线程IntegerTask1: 2
线程IntegerTask2: 2
线程StringTask1: aaaa
线程StringTask2: aaaa
线程IntegerTask2: 3
线程IntegerTask1: 3
线程StringTask1: aaaaa
线程StringTask2: aaaaa
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程IntegerTask2: 0
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程IntegerTask1: 0