1、先说ThreadLocal的实现原理,从实现的原理我们就能搞清楚他的应用场景。
我们先来看ThreadLocal中的域,如下:
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode =
new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;
可以看到只有threadLocalHashCode 是ThreadLocal的实例变量,nextHashCode ,HASH_INCREMENT 是静态变量,可以看到三个变量都是int型的,那么,这我们调用set()方法时传递的对象保存在哪里呢?我们再来看一下set()方法。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
从map.set(this, value);
可以看到,这个map是以ThreadLocal为键,将我们传递的对象放到map中去的。
那么这个map又是什么来路,我们可以看getMap(Thread t);
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
这里可以看到,原来map是Thread类的一个域。
于是我们可以看我们set方法设置的对象其实是保存在了ThreadLocal所在线程的一个ThreadLocalMap 中了,这个Map以ThreadLocal为键。
总结来说就是:每个Thread都有一个ThreadLocalMap的域,用来保存ThreadLocal和Object的映射。
2、我们可以假设这种应用场景,比如说我们需要在每个线程都维持一个对象,那么我们可能会这样设计
class Thread implements Runnable {
Object obj;
}
那要是如果我们需要维持N个对象呢,那么我们可以用一个Object数组
class Thread implements Runnable {
Object obj[];
}
其实Object数组就是一种key为整型,值为Object的Map结构
class Thread implements Runnable {
Map<Integer,Object>;
}
JDK只是用ThreadLocal作为键而已,我们用Integer。
3、应用场景:数据库连接
private static ThreadLocal<Connection> connectionHolder
= new ThreadLocal<Connection>() {
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection() {
return connectionHolder.get();
}
注意:DriverManager.getConnection(DB_URL);
这里每次返回的是一个内容一样,但是占用不同内存的Connection。
网上很多人说,ThreadLocal不是用来解决共享对象的多线程访问问题的,其实也可以看做是,就上面的例子而言,Connection不是线程安全的,那么,我们给每个线程都设置一个内容一样的Connection,线程安全的问题不就解决了吗。这是一种以“空间换取时间“的做法。