ThreadLocal是什么呢?它并不是一个本地的Thread,而是Thread局部变量。它的功能非常简单,就是为每一个使用该变量的县城都提供一个变量值的副本,是Java中一种较为特殊绑定机制,每一个线程都可与独立地改变自己的副本,而不会和其他线程的副本冲突。放到ThreadLocal中的东西,在线程的任何地方取出,都是唯一的一份,而这唯一的一份,只为此线程自己所有。
那么,ThreadLocal是怎样做到为每一个线程维护变量的副本呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。而Key就是线程,Value就是存储的变量副本。
ThreadLocal使用场合主要解决多线程中数据因并发产生不一致和在同一线程始终获取唯一实例的问题(分层中使用)。
ThreadLocal为每个线程中的并发访问的数据提供一个副本,通过副本来运行业务,是一个以控件换取时间的做法。ThreadLocal不能使用原子模型,只能使用Object类型。ThreadLocal的使用比synchronized要简单的多。
ThreadLocal和Synchronized都用于解决线程并发访问,但二者有本质的区别:
Synchronized利用锁的机制,使得一个变量在同一时刻,只能有一个线程进行访问。而ThreadLocal是为每一个线程都独立的提供了一份副本,也就隔离了线程对数据的共享,SYnchronized用于在多个线程通信时能够获得数据分享。
也就是说前者用于数据隔离,后者用于数据共享。二者不能互换,因为二者处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。
正是由于ThreadLocal保存每一个线程的变量副本,那么从ThreadLocal中获取的变量都是同一个变量,正是由于这一,那么把变量放到ThreadLocal中,就可以避免对象的传递。
例如:在业务逻辑层调用经ThreadLocal封装好的方法新建Connection实例,那么在DAO层也用此方法获取Connection实例就会是同一个实例,因为虽然处于不同的层,但是是位于同一个线程。
下面是一个用ThreadLocal封装Connection的实例:
public class ConnectionManager { private static ThreadLocal<Connection> connectionHolder=new ThreadLocal<Connection>(); /** * 得到Connection * @return */ public static Connection getConnection(){ Connection conn=connectionHolder.get(); if(conn==null){ conn=DBUtil.getConn(); connectionHolder.set(conn); } return conn; } public static void closeConnection(){ Connection conn=connectionHolder.get(); if(conn!=null){ try { conn.close(); connectionHolder.remove(); } catch (SQLException e) { e.printStackTrace(); } } } }
经过这样封装Connection,在业务逻辑层创建Connection实例,开启事务后,就不用将Connection进行传递,只需要在DAO层用此类中的getConnection获取到再BLL层创建的Connection即可。从而使得接口的定义更加具有通用性。
最后,再说一下ThreadLocal的一半使用步骤吧:
1.在类中创建一个ThreadLocal对象XxxxHolder,用来保存线程间需要处理的对象。
2.在类中创建一个getXxx()方法,方法中判断XxxxHolder.get()出来的对象是否为null,如果为null那么new()一个放到XxxxHolder中,如果存在,直接返回
3.如果你用ThreadLocal维护的是类似于Connection这样的对象,那么为它创建一个销毁的方法吧,不然你会后悔的。
ThreadLocal的使用才露尖尖角,更多的使用,有待日后的需求。