1 ThreadLocal
1.1 为什么要有ThreadLocal
每次执行数据库方法时需要获取一个连接,如果每次都去重新获取连接,那么想要实现事务比较麻烦,这个时候就可以使用ThreadLocal
private static int insert(String stu) {
Connection coon = getConn();
int i = 0;
String sql = "insert into stu(name, sex, age) values(?,?,?)"
PreparedStatement pstmt;
try{
pstmt = (PreparedStatement) conn.prepareStatement(sql);
pstmt.setString(1, stu);
i = pstmt.executeUpdate();
pstmt.close();
}catch(SQLException e){
e.printStackTrace();
}
return 1;
}
ThreadLocal类提供线程局部变量。可以用于在一个线程中进行跨方法的变量传递
1.2 ThreadLocal的使用
public class UseThreadLocal {
static ThreadLocal<String> threadLocal = new ThreadLocal<>();
static ThreadLocal<Integer> threadLocal2 = new ThreadLocal<>();
public void startThArray(){
Thread[] runs = new Thread[3];
for (int i = 0; i < runs.length ; i++) {
new TestThread(i).start();
}
}
public static class TestThread extends Thread{
int id;
public TestThread(int id){
this.id = id;
}
public void run(){
String thName = Thread.currentThread().getName();
threadLocal.set("thread_" + id);
if (id == 2){
threadLocal2.set(id);
}
System.out.println(thName + ":" + threadLocal.get());
System.out.println(thName + ":" + threadLocal2.get());
}
}
public static void main(String[] args) {
UseThreadLocal useThreadLocal = new UseThreadLocal();
useThreadLocal.startThArray();
}
}
ThreadLocal的实现方式
ThreadLocal是在Thread里边使用ThreadLocalMap作为成员变量去实现的
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();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
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();
}
}
1.2.1 Hash冲突解决
开放定址法
基本思想是,出现冲突后按照一定算法查找一个空位置存放,根据算法的不同又可以分为线性探测再散列、二次探测再散列、伪随机探测再散列。
线性探测再散列即依次向后查找;二次探测再散列, 即依次向前后查找增量为 1、2、3的二次方;伪随机,顾名思义就是随机产生一个增量位移。
ThreadLocal解决hash冲突使用的开放定址法中的线性探测再散列
链地址法
这种方法的基本思想是将所有哈希地址为 i 的元素构成一个称为同义词链的 单链表, 并将单链表的头指针存在哈希表的第 i 个单元中, 因而查找、插入和删除主要在同一链表中进行。 链地址法适用于经常进行插入和删除的情况。 Java里的 HashMap 用的就是链地址法,为了避免 hash 洪水攻击,1.8 版本开始还引入了红黑树。
再哈希法
这种方法是同时构造多个不同的哈希函数: Hi=RH1(key) i=1 ,2 ,… ,k ,当哈希地址 Hi=RH1(key)发生冲突时,再计算 Hi=RH2(key)……,直到冲突不再产生。这种方法不易产生聚集,但增加了计算时间。
公共溢出表
这种方法的基本思想是: 将哈希表分为基本表和溢出表两部分, 凡是和基本 表发生冲突的元素, 一律填入溢出表。
1.3 ThreadLocal内存泄漏分析
如果在thread的run方法中设置了ThreadLocal后,这个线程只要存在,那么他Entry下的value指向的对象不会被回收,就会导致内存泄漏。所以在使用ThreadLocal时,如果不使用了,就需要手动remove掉
1.4 ThreadLocal线程安全问题
如果ThreadLocal设置对象,当他们对象的引用指向同一个对象实例,就会产生一个线程对该数据的修改修改了其他线程的ThreadLocal的修改