`
pouyang
  • 浏览: 314304 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java ThreadLocal模式

    博客分类:
  • Java
阅读更多
设计相关 ThreadLocal模
1,ThreadLocal 不是用来解决共享对象的多线程访问问题的,ThreadLocal和多线程并发没有什么关系。ThreadLocal模式是为了解决单线程内的跨类跨方法调用的(robbin)

但是有的地方还是与多线程有点关系的如下:
ThreadLocal不是用来解决对象共享访问问题的
我不太同意这个观点,现在比如有如下的代码。这个format方法有2个线程循环的访问,每次访问完可以放回线程池中,但是因为SimpleDateFormat不是线程安全的类,所以这样访问肯定会出现并发的错误!
public class Foo {
	
	static SimpleDateFormat formator = new SimpleDateFormat("yyMMddHHmmss");
	
	public static String format(Date date) {
		return formator.format(date);
	}

}


那么为了避免并发的错误,可以有如下2中方案,1加synchronized,这样每次只有一个线程访问这个方法,对性能有影响
public static synchronized String format(Date date) {
		return formator.format(date);
	}

2,每次调用这个方法就生成一个新的SimpleDateFormat对象,这样会生成大量的对象,对性能不是很好
public static String format(Date date) {
		SimpleDateFormat formator = new SimpleDateFormat("yyMMddHHmmss");
		return formator.format(date);
	}


那么如果用ThreadLocal就可以解决创建大量对象的问题和并发访问的问题,重复利用SimpleDateFormat对象
public class Foo {
	static ThreadLocal local = new ThreadLocal();
	
	public static String format(Date date) {
		SimpleDateFormat formator = (SimpleDateFormat)local.get();
		if (formator == null) {
			formator = new SimpleDateFormat("yyMMddHHmmss");
			local.set(formator);
		}
		return formator.format(date);
	}

}



2 通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的

3 ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本。通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的。

4 如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题

我们来看一下 ThreadLocal的set方法的源码
/**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to 
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

得到当前的线程,并且得到当前线程的ThreadLocalMap,把对象放到当前线程的ThreadLocalMap中,如果另外来了一个线程,就会把对象放进到她自己的线程ThreadLocalMap 中。
再看get方法


/**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }

没错吧,从自己线程的getMap(t);的ThreadLocalMap 中得到刚set进去的对象。


来看一下Hibernate源码
private static final ThreadLocal threadSession = new ThreadLocal();   
  
public static Session getSession() throws InfrastructureException {   
    Session s = (Session) threadSession.get();   
    try {   
        if (s == null) {   
            s = getSessionFactory().openSession();   
            threadSession.set(s);   
        }   
    } catch (HibernateException ex) {   
        throw new InfrastructureException(ex);   
    }   
    return s;   
}  



可以看到在getSession()方法中,首先判断当前线程中有没有放进去session,如果还没有,那么通过sessionFactory().openSession()来创建一个session,再将session set到线程中,实际是放到当前线程的ThreadLocalMap这个map中,这时,对于这个session的唯一引用就是当前线程中的那个ThreadLocalMap(下面会讲到),而threadSession作为这个值的key,要取得这个session可以通过threadSession.get()来得到,里面执行的操作实际是先取得当前线程中的ThreadLocalMap,然后将threadSession作为key将对应的值取出。这个session相当于线程的私有变量,而不是public的。
显然,其他线程中是取不到这个session的,他们也只能取到自己的ThreadLocalMap中的东西。要是session是多个线程共享使用的,那还不乱套了。
试想如果不用ThreadLocal怎么来实现呢?可能就要在action中创建session,然后把session一个个传到service和dao中,这可够麻烦的。或者可以自己定义一个静态的map,将当前thread作为key,创建的session作为值,put到map中,应该也行,这也是一般人的想法,但事实上,ThreadLocal的实现刚好相反,它是在每个线程中有一个map,而将ThreadLocal实例作为key,这样每个map中的项数很少,而且当线程销毁时相应的东西也一起销毁了,不知道除了这些还有什么其他的好处。

总之,ThreadLocal不是用来解决对象共享访问问题的,而主要是提供了保持对象的方法和避免参数传递的方便的对象访问方式。归纳了两点:
1。每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。
2。将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。

当然如果要把本来线程共享的对象通过ThreadLocal.set()放到线程中也可以,可以实现避免参数传递的访问方式,但是要注意get()到的是那同一个共享对象,并发访问问题要靠其他手段来解决。但一般来说线程共享的对象通过设置为某类的静态变量就可以实现方便的访问了,似乎没必要放到线程中。

ThreadLocal的应用场合,我觉得最适合的是按线程多实例(每个线程对应一个实例)的对象的访问,并且这个对象很多地方都要用到。

ThreadLocal Memory Leak

ThreadLocal is implemented as a weak hash map. Each thread has a map instance. The map keys are weak references to the ThreadLocal instances themselves. The map values are the thread local values. Just like with WeakHashMap instances, if your value somehow holds a strong reference to the ThreadLocal key, the garbage collector can't reclaim either until you explicitly set the value to null or even better call remove() on the ThreadLocal instance. This bit me in the ass today when I mixed ThreadLocal with nested classes, instances of which have an implicit strong reference to their nesting instance, and failed to properly clean up afterwards. "Implicit" is synonymous with "easy to forget about." Oops.

ThreadLocal 以空间换时间
Synchornized 以时间换空间
http://www.iteye.com/topic/757641

总述:
1 Threadlocal解决了单线程跨类跨方法的调用(robbin)
2 Threadlocal在某些情况下解决了多线程并发的问题。以空间换了时间,ThreadLocal 以空间换时间 Synchornized 以时间换空间
分享到:
评论

相关推荐

    java 简单的ThreadLocal示例

    java 简单的ThreadLocal示例

    设计模式及ThreadLocal资料

    设计模式及ThreadLocal详细讲解资料,想要学习java或者提升自己技术的同学可以下载观看

    java设计模式视频教程-百度网盘地址.txt

    单例模式、工厂模式、装饰者模式、ThreadLocal设计模式、代理模式等

    java事务 - 模板设计模式

    Template模板设计模式改造threadlocal控制事务

    java单例模式看这一篇就够了

    深入分析java单例模式什么是单例模式单例模式的常见写法一、饿汉式单例优点缺点示例二、懒汉式单例示例1(普通写法)示例2(synchronized写法)示例3(DCL写法)示例4(内部类写法)三、注册式单例示例1(容器式)示例2(枚举式...

    Java并发编程实战

    4.2.1 Java监视器模式 4.2.2 示例:车辆追踪 4.3 线程安全性的委托 4.3.1 示例:基于委托的车辆追踪器 4.3.2 独立的状态变量 4.3.3 当委托失效时 4.3.4 发布底层的状态变量 4.3.5 示例:发布状态的车辆追踪...

    java面试题及技巧4

    │ 164个完整Java代码.zip │ J2EE综合--Struts常见错误的全面汇总.txt │ java程序员面试资料.zip │ JAVA笔试题(上海释锐).pdf │ MIME简介.txt │ SCJP试题详解.pdf │ SQL面试题_心灵深处.htm │ Struts+...

    实战Java高并发程序设计(第2版)PPT模板.pptx

    4锁的优化及注意事项 4.1有助于提高锁性能的几点建议 4.2java虚拟机对锁优化所做的努力 4.3人手一支笔:threadlocal 4.4无锁 4.5有关死锁的问题 4.2Java虚拟机对锁优化所做的努力 4.3人手一支笔:ThreadLocal 4.4...

    java面试题目与技巧1

    │ 164个完整Java代码.zip │ J2EE综合--Struts常见错误的全面汇总.txt │ java程序员面试资料.zip │ JAVA笔试题(上海释锐).pdf │ MIME简介.txt │ SCJP试题详解.pdf │ SQL面试题_心灵深处.htm │ Struts+...

    java面试题以及技巧

    │ 164个完整Java代码.zip │ J2EE综合--Struts常见错误的全面汇总.txt │ java程序员面试资料.zip │ JAVA笔试题(上海释锐).pdf │ MIME简介.txt │ SCJP试题详解.pdf │ SQL面试题_心灵深处.htm │ Struts+...

    Java常见面试题208道.docx

    面试题包括以下十九部分:Java 基础、容器、多线程、反射、对象拷贝、Java Web 模块、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、Mybatis、RabbitMQ、Kafka、Zookeeper、MySql...

    Java 并发编程实战

    4.2.1 Java监视器模式 4.2.2 示例:车辆追踪 4.3 线程安全性的委托 4.3.1 示例:基于委托的车辆追踪器 4.3.2 独立的状态变量 4.3.3 当委托失效时 4.3.4 发布底层的状态变量 4.3.5 示例:发布状态的车辆追踪...

    经典JAVA.EE企业应用实战.基于WEBLOGIC_JBOSS的JSF_EJB3_JPA整合开发.pdf

    中文名: 经典Java EE企业应用实战--基于WebLogic/JBoss的JSF+EJB 3+JPA整合开发 原名: 经典Java EE企业应用实战--基于WebLogic/JBoss的JSF+EJB 3+JPA整合开发 作者: 李刚 资源格式: PDF 版本: 第一版 出版社: 电子...

    Java初级、中级、高级面试题及答案

    事务\事务隔离级别\Mysql默认隔离级别\串行化\存储引擎Innodb\Myisam\Inodb锁机制\...Java异常\反射\ThreadLocal\内存泄漏\序列化和反序列化\ArrayList\HashMap\正则表达式\设计模式\linux指令\多线程\Redis\三大排序\

    史上最全java面试,103项重点知识,带目录

    51. ThreadLocal 是什么?有哪些使用场景? 20 52.说一下 synchronized 底层实现原理? 20 53. synchronized 和 volatile 的区别是什么? 21 54. synchronized 和 Lock 有什么区别? 21 55. synchronized 和 ...

    java面试题及技巧3

    │ 164个完整Java代码.zip │ J2EE综合--Struts常见错误的全面汇总.txt │ java程序员面试资料.zip │ JAVA笔试题(上海释锐).pdf │ MIME简介.txt │ SCJP试题详解.pdf │ SQL面试题_心灵深处.htm │ Struts+...

    java面试题以及技巧6

    │ 164个完整Java代码.zip │ J2EE综合--Struts常见错误的全面汇总.txt │ java程序员面试资料.zip │ JAVA笔试题(上海释锐).pdf │ MIME简介.txt │ SCJP试题详解.pdf │ SQL面试题_心灵深处.htm │ Struts+...

    Java并发编程原理与实战

    Future设计模式实现(实现类似于JDK提供的Future).mp4 Future源码解读.mp4 ForkJoin框架详解.mp4 同步容器与并发容器.mp4 并发容器CopyOnWriteArrayList原理与使用.mp4 并发容器ConcurrentLinkedQueue原理与使用....

    java8源码-ac_babel:一些后端学习笔记整理

    设计模式 java 并发 public class Foo { // SimpleDateFormat is not thread-safe, so give one to each thread private static final ThreadLocal formatter = new ThreadLocal(){ @Override protected ...

Global site tag (gtag.js) - Google Analytics