是什么?
ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread , 它类似(Map),用来存储当前运行线程及对应的变量。
在WEB应用中每次Http请求,都相当于从线程池取一个空闲线程对请求的方法作处理。此时当前线程的所有方法中Thread.currentThread()都是一样,然后我们可以把它假想为Map的key,value可以存一个在整个请求中都用到的变量。
好处
1:避免了传参
例如在dao层 **userDao**下有_save(User user)_,_update(Department department_)两种方法,
service中**accessionService**的_accession_方法
boolean accssion(User user, Department department, Connection conn) {
conn.begin();
save(user);
update(department);
conn.commint();
}
为了保证数据一致性,必须在service层添加事务,那么每有一个新的连接(请求),必须要传一个conn参数,做法不好,污染api。
2:解决了高并发中对资源的争夺(牺牲空间换取时间与synchronize相反)。
ThreadLocal源码如下
//一般我们用ThreadLocal,通过调用get(),对其初始化
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")
T result = (T)e.value;
return result;
}
}
return setInitialValue(); //初始化ThreadLocalMap
}
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;
}
//ThreadLocalMap其实是一个entry(静态内部类)继承了WeakReference,是一个弱关联,并非数组
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
//改变当前线程绑定值
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//map.set()具体实现,根据当前线程key定位到entry[i],再改变entry[i]对应的值为value
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
应用
在spring切面方法中,我们没有办法直接从ProceedingJoinPoint中获取request和response对象,但是我们要对request的传递参数处理,同时也要用response添加cookie,实现重定向等功能。
//过滤器,每次请求过来都经过这个过滤器处理
public class GetContent implements Filter {
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
SysContent.setRequest((HttpServletRequest) arg0);
SysContent.setResponse((HttpServletResponse) arg1);
arg2.doFilter(arg0, arg1);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}
//把response、request与当前线程绑定,具体实现
public class SysContent {
private static ThreadLocal<HttpServletRequest> requestLocal= new ThreadLocal<HttpServletRequest>();
private static ThreadLocal<HttpServletResponse> responseLocal= new ThreadLocal<HttpServletResponse>();
public static HttpServletRequest getRequest() {
return (HttpServletRequest)requestLocal.get();
}
public static void setRequest(HttpServletRequest request) {
requestLocal.set(request);
}
public static HttpServletResponse getResponse() {
return (HttpServletResponse)responseLocal.get();
}
public static void setResponse(HttpServletResponse response) {
responseLocal.set(response);
}
public static HttpSession getSession() {
return (HttpSession)((HttpServletRequest)requestLocal.get()).getSession();
}
}
@Component
@Aspect
public class ActionShareAspect {
private static final Logger logger = LoggerFactory.getLogger(ActionLogAspect.class);
//配置切入点
@Pointcut("execution(public * com.xx.xx.*.*(..))") // 配置切入点
public void pointcut(){}
@Around("pointcut()")
public Object aroundMethod(ProceedingJoinPoint pjd) {
//request、response获取
HttpServletRequest request = SysContent.getRequest();
HttpServletResponse response = SysContent.getResponse();
//执行目标方法
String methodName = pjd.getSignature().getName();
Object result = null;
try {
//...略
//返回通知
result = pjd.proceed();
return result;
} catch (Exception e) {
//...
}
}
回顾来思考最初的问题:关于spring的事务。我们可以把每次请求从数据连接池获取的connection绑定到ThreadLocal上,那么就解决了service中事务的问题。当然这里要对service进行切面编程,使用代理,下次博客再细说。