一、ThreadLocal概念:
- ThreadLocal的实例代表了一个线程局部的变量
- 它采用采用空间来换取时间的方式,解决多线程中相同变量的访问冲突问题。
- 实现原理:由下方源码可知,ThreadLocal实际上是用当然的Thread对象为键使用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;
}
二、ThreadLocal案例
1、原始版本:
public class ThreadLocalTestBefore {
//创建ThreadLocal对象
private static ThreadLocal<ThreadLocalData> tl = new ThreadLocal<ThreadLocalData>();
public static void main(String[] args) {
创建两个线程分别存储自己的数据并使用数据
new Thread(new Runnable() {
@Override
public void run() {
int data = new Random().nextInt();
System.out.println(Thread.currentThread().getName()+" "+data);
tl.set(new ThreadLocalData("000",data));
new ThreadLocalTestBefore.A().a();
new ThreadLocalTestBefore.B().b();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
int data = new Random().nextInt();
System.out.println(Thread.currentThread().getName()+" "+data);
tl.set(new ThreadLocalData("111",data));
new ThreadLocalTestBefore.A().a();
new ThreadLocalTestBefore.B().b();
}
}).start();
}
//A、B类分别为使用线程局部变量的数据
static class A{
public void a(){
System.out.println("A from "+ Thread.currentThread().getName()+" "+tl.get().getName()+" "+tl.get().getAge());
}
}
static class B{
public void b(){
System.out.println("B from "+ Thread.currentThread().getName()+" "+tl.get().getName()+" "+tl.get().getAge());
}
}
}
//存储线程局部变量的类
class ThreadLocalData{
private String name;
private int age;
public ThreadLocalData(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
2、改良版本:
改良原因:因为每个线程该局部变量只有一个,所以可以把该对象设计为单例模式,并封装在内部。
public class ThreadLocalTest {
public static void main(String[] args) {
//创建两个线程分别存储自己的数据并使用数据
new Thread(new Runnable() {
@Override
public void run() {
int data = new Random().nextInt();
System.out.println(Thread.currentThread().getName()+" "+data);
ThreadLocalData.getThreadInstance().setName("000");
ThreadLocalData.getThreadInstance().setAge(data);
new A().a();
new B().b();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
int data = new Random().nextInt();
System.out.println(Thread.currentThread().getName()+" "+data);
ThreadLocalData.getThreadInstance().setName("111");
ThreadLocalData.getThreadInstance().setAge(data);
new A().a();
new B().b();
}
}).start();
}
//A、B类分别为使用线程局部变量的数据
static class A{
public void a(){
System.out.println("A from "+ Thread.currentThread().getName()+" "+ThreadLocalData.getThreadInstance().getName()+" "+ThreadLocalData.getThreadInstance().getAge());
}
}
static class B{
public void b(){
System.out.println("B from "+ Thread.currentThread().getName()+" "+ThreadLocalData.getThreadInstance().getName()+" "+ThreadLocalData.getThreadInstance().getAge());
}
}
}
//模仿单例设计模式
class ThreadLocalData{
private static ThreadLocal<ThreadLocalData> tl = new ThreadLocal<ThreadLocalData>();
//构造方法私有
private ThreadLocalData(){};
//已设计成线程内数据共享模式
public static ThreadLocalData getThreadInstance(){
ThreadLocalData instance = tl.get();
if(instance == null){//懒汉设计模式
instance = new ThreadLocalData();
tl.set(instance);
}
return instance;
}
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
三、ThreadLocal应用场景
最常见的ThreadLocal使用场景为 用来解决 数据库连接(不考虑连接池)、Session管理等。
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection() {
return connectionHolder.get();
}
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;
}