面试的时候问了threadlocal的用法,之前只是了解到threadlocal是为了解决多线程并发问题,具体什么原理不是很清楚,重新学习了一下。
threadlocal是thread的一个局部变量,threadlocal里面有一个threadlocalMap,这个map里面,key是threadlocal变量,value是用户存放的变量。其实它解决多线程并发问题的根本原因是,threadlocal是thread的局部变量,既然是局部变量,别的线程自然是访问不到。
我在面试时列举了一个threadlocal的用法,是在获取jedis客户端的时候,代码是这样写的:
ThreadLocal<Jedis> threadLocal = new ThreadLocal<Jedis>()
....
Jedis jedis = threadLocal.get();
if (jedis == null) {
jedis = jedisPool.getResource();
}
我看到这段代码时,我记得看了日常的set和get操作,这个都是没有什么作用的,没有初始化的操作,每次获取都是null,那为什么还要这么获取一次呢。
查了一下代码,初始化的地方有两处,一处是pipeline的操作,一处是事务的操作。
事务的初始化:
上面的获取连接操作后增加一个lockConn操作
...
public void lockConn(Jedis conn) {
threadLocal.set(conn);
}
...
事务内业务逻辑
...
事务处理完毕后调用以下释放连接的操作(感觉这里有漏洞)
public void unLockConn() {
threadLocal.set(null);
}
然后有了一点眉目,事务的操作需要在同一个connection上,那么需要在每次拿连接的时候都拿到的是当前线程的同一个connection,用threadllocal再合适不过。事务完成的时候释放threadlocal里的变量就可以。
pipeline的操作也是一样的道理,需要用同一个连接批量传输数据。然后等待服务端的返回结果。
到这里,threadlocal的应用场景是明了了,主要是把连接放在threadlocal里,这样拿连接的时候还是用的老连接,保证了事务的操作控制在一个连接上;但是多线程并发这一块好像没有体现,redis本身虽然是单线程,但业务如果是多线程操作redis的话,就是线程不安全的了。
在网上看到了一个有意思的例子,感觉说的很贴切,虽然目前还没有在项目中发现应用场景:
public class ThreadLocalTest {
static class ResourceClass {
//初始化两个threadlocal实例
public final static ThreadLocal<String> RESOURCE_1 = new ThreadLocal<String>();
public final static ThreadLocal<String> RESOURCE_2 = new ThreadLocal<String>();
}
static class A {
//两个更新value的值
public void setOne(String value) {
ResourceClass.RESOURCE_1.set(value);
}
public void setTwo(String value) {
ResourceClass.RESOURCE_2.set(value);
}
}
static class B {
public void display() {
System.out.println(ResourceClass.RESOURCE_1.get() + ":" + ResourceClass.RESOURCE_2.get());
}
}
public static void main(String []args) {
final A a = new A();
final B b = new B();
for(int i = 0 ; i < 15 ; i ++) {
final String resouce1 = "线程-" + I;
final String resouce2 = " value = (" + i + ")";
new Thread() {
public void run() {
try {
a.setOne(resouce1);
a.setTwo(resouce2);
b.display();
}finally {
ResourceClass.RESOURCE_1.remove();
ResourceClass.RESOURCE_2.remove();
}
}
}.start();
}
}
}
贴一下结果
线程-0: value = (0)
线程-8: value = (8)
线程-7: value = (7)
线程-6: value = (6)
线程-10: value = (10)
线程-11: value = (11)
线程-12: value = (12)
线程-14: value = (14)
线程-13: value = (13)
线程-1: value = (1)
线程-2: value = (2)
线程-3: value = (3)
线程-9: value = (9)
线程-4: value = (4)
线程-5: value = (5)
这个例子确实证明了,在多线程运行的时候,a和b的值是一致的,确实没有相互影响。具体这一块的应用场景,等见到了再补充。