ThreadLocal浅析
来源:互联网 发布:博克软件 收入 编辑:程序博客网 时间:2024/05/15 13:09
web的线程安全问题
因为servlet是线程不安全的,而web容器是个典型的多线程的环境,对于http每次的请求,web容器都会分配一个线程,这样就会产生线程安全问题,为了解决这个问题,Sun公司在角度看1.2提供了ThreadLocal的API,本文就这个ThreadLocal解决web的线程安全问题,做一个总结。
ThreadLocalMap
全面提到解决web的线程安全问题需要用到呢ThreadLocal,那么ThreadLocal到底是什么呢?
ThreadLocal并不是一个线程Thread,而是通过操作ThreadLocalMap来隔离线程与线程,继而解决线程安全问题。通过查看java.lang.Thread的源码,可以看到这样一段代码
ThreadLocal.ThreadLocalMap threadLocals = null;
通过这段代码,可以明显的看出,每个Thread都会创建一个ThreadLocalMap,这样每个线程都会拥有一个ThreadLocalMap,于是可以通俗的将这个ThreadLocalMap看做是Thread的一个“副本”。
再看看java.lang.ThreadLocal的源码:
//java.lang.ThreadLocal的getMap方法 ThreadLocalMap getMap(Thread t) { //返回t的threadLocals return t.threadLocals; } //java.lang.ThreadLocal的set方法 public void set(T value) { Thread t = Thread.currentThread(); //获得当前线程的ThreadLocalMap ThreadLocalMap map = getMap(t); //如果不为空就直接使用,如果为空就创建一个 if (map != null) //将当前的ThreadLocal的实例,作为key存储在ThreadLocalMap中 //若当前Thread创建了多个ThreadLocal的实例,就可以 //通过map来隔离各个ThreadLocal的实例 map.set(this, value); else createMap(t, value); } //java.lang.ThreadLocal的get方法,获得当前实例的ThreadLocalMap的value值 public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") //获得当前实例的ThreadLocalMap的value值 T result = (T)e.value; return result; } } //返回null return setInitialValue(); } private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; } protected T initialValue() { return null; }
通过上上面的代码可以很容易的看得出来,ThreadLocal的set与get方法都是对ThreadLocalMap的操作.通过对源码的分析,我们可以总结出来以下几点结论:
- ThreadLocalMap是在Thread创建的时候就已经创建了的,而且每个Thread拥有各自的ThreadLocalMap,ThreadLocalMap并不是在ThreadLocal的创建时创建的
- ThreadLocalMap的值时在ThreadLocal使用set与get方法时进行赋值的,在调用之前,ThreadLocalMap是null
- ThreadLocalMap每次都是通过ThreadLocal的实例作为key,然后进行存储
- ThreadLocal通过两个方面来实现线程的隔离:
- 纵向隔离:线程与线程之间,由于ThreadLocal操作的是不同的ThreadLocalMap而隔离数据之间的访问
- 横向隔离:同一个线程,对于不同的资源,ThreadLocal通过对不同ThreadLocal实例作为key存储在ThreadLocalMap中,而实现统一线程不同资源的隔离
ThreadLocalMap与synchronized的比较
前面已经提到了ThreadLocal实现线程的隔离是通过ThreadLocalMap,而ThreadLocalMap又可以看做是Thread的一个“副本”,这实际上就是通过用“空间换取时间”的方式来解决多线程的线程安全问题。
而相对应的synchronized关键字,它依靠的是JVM的锁机制来实现临界区的函数或者变量在访问中的原子性。在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。此时,被用做“锁机制”的变量是多个线程所共享的。
对ThreadLocal与synchronized的比较可以用一句话来总结:ThreadLocal实质就是用“空间换取时间”的方式,为每个线程提供一个变量的“副本”,让每个线程访问各自的变量副本;synchronized关键字就是用“时间换取空间”的方式,提供一份变量,让不同的线程排队访问
ThreadLocal的一个简单案例
前面已经总结了ThreadLocal可以解决多线程的线程安全问题,那么ThreadLocal到底该怎么使用呢,我之前写过一篇DBUtils的博客,这里也针对DBUtils使用ThreadLocal进行一次简单的改造,写一段可以开启关闭事务的DBUtils,代码如下:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import org.apache.commons.dbutils.DbUtils; import com.mchange.v2.c3p0.ComboPooledDataSource; public class TransactionManagerDBUtils { private TransactionManager() { } //--数据源,整个程序中都只有这一个数据源 private static DataSource source = new ComboPooledDataSource(); //--是否开启事务的标记 private static ThreadLocal<Boolean> isTran_local = new ThreadLocal<Boolean>(){ //重写ThreadLocal的initialValue的方法 @Override protected Boolean initialValue() { //--最开始false,表明默认不开启事务 return false; } }; //--保存真实连接的代理连接,改造过close方法 private static ThreadLocal<Connection> proxyConn_local = new ThreadLocal<Connection>(){}; //--保存真实连接 private static ThreadLocal<Connection> realconn_local = new ThreadLocal<Connection>(){}; /** * 开启事务的方法 * @throws SQLException */ public static void startTran() throws SQLException{ isTran_local.set(true);//--设置事务标记为true //--创建连接,所有当前线程中的数据库操作都基于这个conn final Connection conn = source.getConnection(); //--开启事务 conn.setAutoCommit(false); //--为了方便后续关闭连接,将这个连接保存起在当前线程中 realconn_local.set(conn); //--由于一个事务需要执行多条sql,每个sql执行过后都关闭连接 //这样一来后续的sql没法执行 //所以这个地方法改造close方法,使他不能关闭连接 Connection proxyConn = (Connection) Proxy.newProxyInstance( conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler(){ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("close".equals(method.getName())){ return null; }else{ return method.invoke(conn, args); } } }); //将经过动态代理的proxyConn存入到proxyConn_local中 proxyConn_local.set(proxyConn); } /** * 提交事务 */ public static void commit(){ DbUtils.commitAndCloseQuietly(proxyConn_local.get()); } /** * 回滚事务 */ public static void rollback(){ DbUtils.rollbackAndCloseQuietly(proxyConn_local.get()); } /** * 这个方法应该做到: * 如果没有开启过事务,则返回最普通的数据源 * 如果开启过事务,则返回一个改造过getConnection方法的数据源 * 这个方法改造后每次都返回同一个开启过事务的Connection * @return * @throws SQLException */ public static DataSource getSource() throws SQLException{ if(isTran_local.get()){ //--如果开启过事务,则返回改造的DataSource //改造为每次调用getConnection都返回同一个开启过事务的Conn return (DataSource)Proxy.newProxyInstance( source.getClass().getClassLoader(), source.getClass().getInterfaces(), new InvocationHandler(){ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("getConnection".equals(method.getName())){ return proxyConn_local.get(); }else{ return method.invoke(source, args); } } }); }else{ //--没有开启过事务,返回普通的数据源 return source; } } /** * 释放资源 */ public static void release(){ DbUtils.closeQuietly(realconn_local.get()); //--之前连接是没有关闭的在release的时候真正的关闭连接 realconn_local.remove(); proxyConn_local.remove(); isTran_local.remove(); } }
- ThreadLocal浅析
- ThreadLocal浅析
- 浅析ThreadLocal
- ThreadLocal浅析
- ThreadLocal浅析
- ThreadLocal浅析
- ThreadLocal浅析
- 浅析ThreadLocal
- 浅析ThreadLocal
- 浅析ThreadLocal
- ThreadLocal浅析
- 浅析 ThreadLocal
- 浅析 ThreadLocal
- ThreadLocal浅析
- ThreadLocal浅析
- ThreadLocal浅析
- Java ThreadLocal使用浅析
- Java ThreadLocal使用浅析
- 浅谈vector中的push_back(无realloc)和 创建vector(n) 后通过下标赋值
- 使用PHP制作多文件上传
- 泛型
- 自主悬停&高度融合
- maven配置
- ThreadLocal浅析
- 二叉树的链式结构
- 如何使用JPanel中间容器
- 简单密码明文和16,32MD5密码对应
- js:面向对象编程,带你认识封装、继承和多态
- Web开发的前期学习HTML和CSS的标签总结
- 堆 续4
- 关于Floyd最短路径的path图的生成问题
- 欢迎使用CSDN-markdown编辑器