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则用于线程间数据的隔离。