保证事务不失效的前提是连接要是同一个,这样的话为了保证连接时同一个就要每个方法都需要传入connection对象,那么有没有更好的办法呢?当然有了,那就是绑定连接到当前线程,而spring就是这么做的。
描述
使用
原理
如果让你实现你会怎么做呢,有一种做法是实现一个Map,key为对应线程对象,value是副本,考虑到并发问题再加上锁。但这样会存在性能问题,如下图所示:
最好的做法是这个副本就是线程本身的一部分,除此之外每个线程的副本数也是不固定的,如下是ThreadLocal具体实现:
其中ThreadLocalMap和HashMap的不同点在于hash冲突的解决,它采用开放地址法,当发现冲突时,就按照算法找一个新的空的位置。
开放定址法
泄漏
ThreadLocal使用不当会造成内存泄漏问题。
ThreadLocal内存泄漏原因如下:
当run方法执行结束后,threadLocalLV不在引用ThreadLocal()方法,而ThreadLocal()和key又是弱引用gc时会被回收掉,而这个对应的Entry对象又是被强引用着导致无法回收,且该key已经无法再被引用,成为一个无法回收的垃圾对象。
正确使用
存的值不能是同一对象,可以重写初始化方法initialValue()。示例如下:
public class Test implements Runnable {
public static ThreadLocal<Number> value = new ThreadLocal<Number>(){
@Override
protected Number initialValue() {
return new Number(0);
}
};
public static void main(String[] args) {
for (int i = 0; i<5; i++){
new Thread(new Test()).start();
}
}
@Override
public void run() {
Random r = new Random();
Number number = value.get();
number.setNum(number.getNum() + r.nextInt(100));
value.set(number);
System.out.println(Thread.currentThread().getName()+"="+value.get().getNum());
}
private static class Number{
private Integer num;
public Number(Integer num){
this.num = num;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
}
}
解决方法
remove()即可
应用
- 跨方法参数传递
- 微服务链路追踪(traceid的传递)
问题
ThreadLocal和synchronized的区别?