ThreadLocal并不是本地线程的意思,它压根就不是线程,而是线程局部变量,它的功能很简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是java中一种比较特殊的线程绑定机制,每一个线程都可以独立的改变自己的副本,而不会和其他线程的副本冲突。
从线程的角度看,每个线程都保持一个对线程局部变量副本的隐式引用,只要线程是活动的,并且ThreadLocal实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被虚拟机所回收(除非存在对这些副本的其他引用)。
通过ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM为每个运行的线程绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发问题提供了一种隔离机制。
在ThreadLocal类中有一个Map,用于存储每一个线程的副本,这就是ThreadLocal可以维护每一个线程副本的思路。
构造函数:
TreadLocal() : 创建一个线程本地变量。
方法:
get() :返回此线程局部变量的当前线程副本中的值,如果这是线程第一次调用该方法,则创建并初始化该副本。
initialValue() :返回此线程局部变量的当前线程的初始值。最多在每次访问线程来获得每个线程局部变量时调用此方法一次,即线程第一次使用get()方法访问变量的时候,如果线程先调用set()方法,后调用get()方法,则不会调用initialValue()方法。
remove():移除线程局部变量的值。
set(T value):将此线程局部变量的当前线程副本中的值设置为指定值。
注:在程序中一般重写initialValue()方法,以给定一个特定的初始值。
案例一:有两个线程,三个模块,如何在三个模块的同一个线程内共享一个变量。
public class TreadLocalTest {
/*定义一个存放基础数据的ThreadLocal的变量*/
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
/*定义一个存放实体对象的TheadLocal变量*/
private static ThreadLocal<User> user = new ThreadLocal<User>();
public static void main(String[] args) {
/*通过循环来模拟两个线程*/
for (int i=0; i<2; i++){
new Thread(new Runnable() {
@Override
public void run() {
/*产生一个随机数*/
int data = new Random().nextInt(8);
/*打印当前线程产生的随机数*/
System.out.println(Thread.currentThread().getName() + "产生的随机数为:" + data);;
/*把这个随机数存入threadLocal中【这个操作就是把数据存入当前线程中】*/
threadLocal.set(data);
/*从实体对象的ThreadLocal中取出对象设置值*/
User.getThreadIntance().setUid(data);
User.getThreadIntance().setUname("lavimer" + data);
/*从模块A中取出数据*/
new ModuleA().getData();
/*从模块B中取出数据*/
new ModuleB().getData();
}
}).start();
}
}
/**
* 模块A
*/
static class ModuleA{
public void getData(){
/*线程范围内的基础变量*/
System.out.println("A模块在线程:" +Thread.currentThread().getName()+ "中取出数据:" + threadLocal.get());
/*线程范围内的实体变量*/
User user = User.getThreadIntance();
System.out.println("A模块在线程:" +Thread.currentThread().getName()+ "中取出实体数据==>uid:" + user.getUid() +" uname: "+ user.getUname());
}
}
/**
* 模块B
*/
static class ModuleB{
public void getData(){
/*线程范围内的基础变量*/
System.out.println("B模块在线程:" + Thread.currentThread().getName() + "中取出数据:" + threadLocal.get());
/*线程范围内的实体变量*/
User user = User.getThreadIntance();
System.out.println("B模块在线程:" +Thread.currentThread().getName()+ "中取出实体数据==>uid:" + user.getUid() +" uname: "+ user.getUname());
}
}
}
/**
* 实体对象
* @author Liao
*
*/
class User {
private Integer uid;
private String uname;
/*创建user对象的ThreadLocal变量*/
private static ThreadLocal<User> map = new ThreadLocal<User>();
/*私有无参构造函数【从外部不能new对象】*/
private User() {
}
/*通过静态方法来获取实例对象*/
public static User getThreadIntance(){
/*如果集合中有值,就直接取出*/
User instance = map.get();
/*如果集合中没有值*/
if (instance == null){
instance = new User();
/*存到ThreadLocal中*/
map.set(instance);
}
return instance;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
}
从图中我们可以看出使用ThreadLocal可以使得在同一线程中,变量都是同一个,即达到了线程范围内的数据共享。
案例二:Hibernate官方文档中的HibernateUtil类,用于session管理:
public class HibernateUtil {
/*定义SessionFactory*/
private static final SessionFactory sessionFactory;
static {
try {
/*通过默认配置文件hibernate.cfg.xml创建SessionFactory*/
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Exception e) {
e.printStackTrace();
}
}
/*创建线程局部变量Session,用来保存hibernate的Session*/
public static final ThreadLocal session = new ThreadLocal();
/**
* 获取当前线程中的Session
* @return
*/
public static Session currentSession(){
Session s = (Session)session.get();
/*如果Session没有打开,则新开一个Session*/
if (s == null){
s = sessionFactory.openSession();
/*将新开的Session保存到线程局部变量中*/
session.set(s);
}
return s;
}
/**
* 关闭当前线程的Session
*/
public static void closeSession(){
/*获取线程局部变量,并强制转换为Session类型*/
Session s = (Session)session.get();
/*把当前线程的session设置为null*/
session.set(null);
if (s != null){
s.close();
}
}
}
这样做的目的也是为了在线程范围内共享数据。
总而言之:ThreadLocal主要是用来解决多线程中数据因并发而产生不一致的问题,Thread为每个线程中的并发访问数据提供了一个副本,通过访问副本来运行,这样做的缺点是耗费了内存,但是也大大的减少了线程同步所带来的性能消耗,也减少了线程并发控制的复杂度。
ThreadLocal和Synchronized的异同:
1:ThreadLocal和Synchronized都用于解决多线程的并发问题。
2:他们有着本质的区别,Synchronized是利用锁的机制,使变量或代码块在某一时刻只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每一个线程在同一时间访问的都不是同一对象。
3:Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间数据的隔离。