ThreadLocal类的理解

来源:互联网 发布:基站定位数据库 编辑:程序博客网 时间:2024/05/01 23:28

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


0 0
原创粉丝点击