项目开发最常见的模式就是拿着之前开发的工程到新的项目中修修改改,这种开发方式很快,主要功能该有的都有了,只要按照新的需求改改就能用,但是开发不可能一直都是同一拨人干这事,后来的人对整个项目也不会做到100%的了解,这样新近的人就会做一些重复的工作,一两次还没啥,但是多了之后后面开发的人就会被坑死。现在就是开发这种项目过程不多说了。
最近看之前的代码,程序中对内容库session连接处理的地方感觉很奇怪,大致处理方式如下:
//定义Session保存对象 private static Map<String, ThreadLocal<Session>> map = new HashMap<String, ThreadLocal<Session>>(); //获取session方法 public Session get(String targetDocName){ ThreadLocal<Session> threadLocalSession = map.get(map.get(targetDocName)); Session session = null; if(threadLocalSession == null){ threadLocalSession = new ThreadLocal<Session>(); map.put(targetDocName, threadLocalSession); } if(threadLocalSession.get() == null){ session = SessionUtil.getSession(targetDocName); threadLocalSession.set(session); } threadLocalSession.get(); return session.get(); }
因为需要获取多个连接库的session所以最外层用Map保存,但是里面使用ThreadLocal获取session的方式就很奇怪了,我们知道ThreadLocal定义的是线程变量,在一个线程中操作一个ThreadLocal变量是不会影响到这个变量在其他线程中的正常使用的。按照这个思路,程序中设置ThreadLocal变量的值,只会对当前线程有效,所以之后的线程通过threadLocalSession来获取session都会是空值,由于ThreadLocal这个对象的特殊性,造成了他的设计思路与实际的程序运行有出入,当然这里的实现不会影响程序的正确性,只是性能上的问题。
下面我们就来了解下ThreadLocal这个线程变量:
首先需要了解为什么ThreadLocal定义的是一个线程变量(解释下我对线程变量的理解,就是定义的这个变量作用域是当前线程,线程结束该变量随之释放。),我们打开jdk源码查看java.lang.Thread的实现,可以看到有如下实现:
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;
Thread的定义中就包含了一个ThreadLocal的属性,也就是说每一个线程执行的时候都有一个与之对应的ThreadLocal对象属性,看到这里我们大致可以明白为什么会说ThreadLocal是一个线程变量了,但是JDK中到底是如何做到将ThreadLocal设置为一个线程变量的呢,看这个属性的注释,这个变量由ThreadLocal维护,我们就知道答案在ThreadLocal中。
我们看下ThreadLocal中是如何实现get方法的:
/** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */ public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); } /** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @return the map */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
get方法首先获取当前线程的threadLocals变量,这个变量在线程初始化时是空的, 所有在线程中第一次调用ThreadLocal对象的get方法会直接调用setInitialValue方法,setInitialValue方法实现如下:
/** * Variant of set() to establish initialValue. Used instead * of set() in case user has overridden the set() method. * * @return the initial value */ 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; }
这段代码就是将initialValue方法的返回值设置到当前线程,最后返回这个值,但是这个方法的返回值是null。回到我的项目中,由于ThreadLocal对象是直接创建的,所以在后面通过ThreadLocal的get方法都只会等到一个空值。
如果需要ThreadLocal对象有默认初始值,我们只需要重写这个方法就可以了。
//O为自定义类 ThreadLocal<O> threadLocal = new ThreadLocal<O>(){ public O initialValue(){ return new O(); } };
相关推荐
在服务器端,线程池的存在可能导致多个用户的请求被分配到同一个线程处理,因此,如果不注意清理,ThreadLocal变量可能会被不同用户访问到,造成数据泄露。 误区三:每个用户访问会有新的ThreadLocal 理论上来讲,...
ThreadLocal是Java编程语言中...但在使用过程中,开发者需要注意内存管理和避免误解其为一种通用的线程同步机制,以防止潜在问题的发生。了解ThreadLocal的底层实现和工作原理对于优化并发代码和防止内存泄漏至关重要。
描述中提到的“线程局部存储”(TLS,也常被误解为Thread Local Storage)是编程语言中一个重要的特性,特别是在多线程环境下。它允许每个线程拥有自己独立的变量副本,这些副本只对创建它们的线程可见,避免了在...
`ThreadLocal`变量虽然在线程之间隔离,但如果线程池重用线程,之前的线程局部变量可能会影响到后续任务,导致意外的行为。要确保在每次任务开始时清理或重新设置线程局部变量。 6. **不恰当的同步对象选择**: ...
标签“java_interview java_面试_雅虎_360”表明这个压缩包里的内容主要涉及Java语言的面试问题,但同样,雅虎(Yahoo)的提及可能是个误解。我们将重点关注360公司的Java面试要求。 从压缩包中唯一的文件“2016...
了解synchronized、volatile、ThreadLocal等机制,以及ExecutorService和并发工具类。 10. **测试**:编写单元测试和集成测试,确保代码正确性。JUnit是常用的Java测试框架,提倡TDD(测试驱动开发)。 以上只是...
《JavaPuzzlers》是Java领域的一本经典书籍,作者是Joshua Bloch和Neal Gafter,这本书通过一系列精心设计的编程谜题,揭示了Java语言中容易被误解或者不为人知的特性。这些谜题被称为"Java益智类",它们能够帮助...
9.5.1 一个将被实施事务增强的服务接口 9.5.2 使用原始的 TransactionProxyFactoryBean 9.5.3 基于tx/aop命名空间的配置 9.6 使用注解配置声明式事务 9.6.1 使用@Transactional注解 9.6.2 通过AspectJ LTW引入事务切...
9.5.1 一个将被实施事务增强的服务接口 9.5.2 使用原始的 TransactionProxyFactoryBean 9.5.3 基于tx/aop命名空间的配置 9.6 使用注解配置声明式事务 9.6.1 使用@Transactional注解 9.6.2 通过AspectJ LTW引入事务切...