java中ThreadLocal原理及其使用

Thread Local背后的实现机制-原理
1.每个线程都有一个ThreadLocalMap变量,所以可以想到每个线程独立的变量都是通过ThreadLocalMap数据结构来维护的。

//Thread.java 中
public class Thread implements Runnable {

ThreadLocal.ThreadLocalMap threadLocals = null;
}
ThreadLocal{
/**
* 1.获取本地线程 thread.currentThread();
* 2.取得本地线程的threadLocalMap,然后往这个map里塞值。
*/
public void set(T value) {
Thread t = Thread.currentThread(); //从这里可以看出代码执行这里,就会往当前线程设置变量。
ThreadLocalMap map = getMap(t);
if (map != null)
// ThreadLocalMap(ThreadLocal,Object),以ThreadLocal作为key,object作为value。
map.set(this, value);
else
createMap(t, value);
}

 /**
 *
 */
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

}
如何使用Thread Local
了解了ThreadLocal的使用原理,我们先看ThreadLocal类有哪些方法。
initValue()方法
set()方法
get()方法

ThreadLocal{
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//this为ThreadLocal对象
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
}
我们定义如下threadLocal变量。
private ThreadLocal myThreadLocal1 = new ThreadLocal();
private ThreadLocal myThreadLocal2 = new ThreadLocal();
在实际使用过程中
myThreadLocal1.get(),获取的是当前线程中threadLocalMap 里myThreadLocal1为key对应的值。
当调用myThreadLocal1.set(“abc”),自动的把abc设置到当前Thread.ThreadLocalMap()中去了,参考ThreadLocal类型set()方法。

注意我们在Thread中添加的ThreadLocal.ThreadLocalMap 变量在线程结束的时候必须释放掉,请查看Thread类exits方法:

/**
* This method is called by the system to give a Thread
* a chance to clean up before it actually exits.
/
private void exit() {
if (group != null) {
group.threadTerminated(this);
group = null;
}
/
Aggressively null out all reference fields: see bug 4006245 /
target = null;
/
Speed the release of some of these resources */
threadLocals = null;
inheritableThreadLocals = null;
inheritedAccessControlContext = null;
blocker = null;
uncaughtExceptionHandler = null;
}
threadLocal 与thread的同步锁比较
threadLocal 是为每个线程维护一份本地的变量,所以是“空间换时间”的方式。
thread同步锁 访问串行化、对象共享化,是“时间换空间”的方式。

如何正确使用threadLocal
由于threadLocal就是解决多个线程之间互相干扰的情况,每个线程都有他们自己的threadLocalMap数据结构。
所以各个线程的threadLocalMap不能指向同一个引用,即每个线程需要肚子引用记录自己的对象threadLocalMap.set(this,new Object())这样线程之间才不会干扰。

/**

  • 由于是thread中有一个threadLocal.threadlocalMap引用,所以
  • @author shawn

*/
public class SafeThreadLocalThread implements Runnable{

private ThreadLocal myThreadLocal = new ThreadLocal();

public static  int i = 0;

@Override
public void run() {
    Number number = new Number();//每个线程需要使用自己的对象。
    number.setNo(i++);
    
    //将值存储到threadLocal中
    myThreadLocal.set(number);
    
    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    
    System.out.println("number.getNo()" + number.getNo());
}

public static void main(String[] args) {
    ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
    for (int i = 0; i < 5; i++) {
        newCachedThreadPool.execute(new SafeThreadLocalThread());
    }
}

/**
 * 每个number 各不影响因为他是在各个线程里单独new的。
 * number.getNo()2
 *number.getNo()3
 *number.getNo()1
 *number.getNo()4
 *number.getNo()0
 */

}
详细代码请查看:https://github.com/shawnxjf1/J2seCodeExample

不同级别的缓存
1.threadLocal相当于线程级别的缓存,每个线程绑定一个map容器在这个容器里存储该线程里的数据。
2.而我们在代码中经常用到static 变量的缓存,比如如下代码:

class demo {
public static Map demoCache= new Hashmap();//定义jvm级别的缓存
}
3.对于tomcat servlet container,j2ee container 每个容器都有自己的classloader,比如每个tomcat每个应用都有自己的appclassLoader只加载自己的class.
比如如下应用app1,app2可能需要缓存的数据是不一样的,我们希望操作app1 的demoCache时候不影响应用app2的。
此时我们需要的缓存级别就是按容器级别(每个容器的classloader不一样),最常用到的容器级别的工具是:

org.apache.commons.beanutils.ContextClassLoaderLocal{
private final Map valueByClassLoader = new WeakHashMap();
}
在apache beanUtils组建注册convert的时候都是放在per(thread) context classloader级别的缓存里。
当然如果上述代码运行在非容器(意味着只有一个classLoader),其效果就相当于static 变量缓存了。
相关代码见参考列 [beanutils注册convert]。

写完后想法
以前使用ThreadLocal 都是从网上摘抄代码片段,但是当我弄明白ThreadLocal原理自己就能够灵活使用ThreadLocal代码来。弄明白了原理后你就是从脑子出发来表达和实现ThreadLocal信息,而如果你只是copy网上代码说明信息是没有经过脑子的,当然我们不要求所有的技术都弄得很深但是你使用它的时候一定要知道它与其他模块或代码对接的原理。

参考
参考1:beanUtils 注册convert
//ContextClassLoaderLocal 类似于threadLocal
BeanUtilsBean{
private static final ContextClassLoaderLocal
BEANS_BY_CLASSLOADER = new ContextClassLoaderLocal() {
// Creates the default instance used when the context classloader is unavailable
@Override
protected BeanUtilsBean initialValue() {
return new BeanUtilsBean();
}
};

/**
 * 获取当前classloader的BeanutilsBean
 * Gets the instance which provides the functionality for {@link BeanUtils}.
 * This is a pseudo-singleton - an single instance is provided per (thread) context classloader.
 * This mechanism provides isolation for web apps deployed in the same container.
 *
 * @return The (pseudo-singleton) BeanUtils bean instance
 */
public static BeanUtilsBean getInstance() {
    return BEANS_BY_CLASSLOADER.get();
}

/**
 * Sets the instance which provides the functionality for {@link BeanUtils}.
 * This is a pseudo-singleton - an single instance is provided per (thread) context classloader.
 * This mechanism provides isolation for web apps deployed in the same container.
 *
 * @param newInstance The (pseudo-singleton) BeanUtils bean instance
 */
public static void setInstance(final BeanUtilsBean newInstance) {
    BEANS_BY_CLASSLOADER.set(newInstance);
}

}

ConvertUtilsBean{
protected static ConvertUtilsBean getInstance() {
return BeanUtilsBean.getInstance().getConvertUtils();
}
}
深圳网站建设www.sz886.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值