现在的位置: 首页 > 综合 > 正文

ThreadLocal类的理解

2018年06月05日 ⁄ 综合 ⁄ 共 3871字 ⁄ 字号 评论关闭

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

抱歉!评论已关闭.