一、 线程安全问题的由来
1、对于同一个serlvet对象的多个请求,Servlet的service方法将在一个多线程的环境中并发执行。所以Web容器默认采用单实例(单Servlet实例)多线程的方式来处理Http请求。会导致变量访问的线程安全问题。
Servlet对象的运行特性:
1.Servlet对象是一个无状态的单例对象
2.sevlet在不同的线程中运行
3.serlvet的变量在不同的线程中共享。
2、线程安全的概念范畴
线程安全,指的是在多线程环境下,一个类在执行某个方法时,对类的内部实例变量的访问是安全的。因此对以下两类变量,不存在任何线程安全的说法:
1.方法签名中的任何参数变量
2.处于方法内部的局部变量
任何针对上述形式的变量的访问都是线程安全的,因为他们都是处于方法体的内部,由当前的执行线程独自管理。
二、ThreadLocal模式的实现机理
1、ThreadLocal类在维护变量时,实际使用了当前线程(Thread)中的一个叫做ThreadLocalMap的独立副本,每个线程可以独立修改属于自己的副本而不会互相影响,从而隔离了线程和线程。
ThreadLocal本身并不是一个线程,而是通过操作当前线程中的一个内部变量来达到与其他线程隔离的目的。之所以取名为ThreadLocal,所期望表达的含义是其操作的对象是线程的一个本地变量。
ThreadLocalMap跟随着当前的线程而存在。不同的线程Thread,拥有不同的ThreadLocalMap的本地实例变量,这也就是“副本”的含义。
结论:1.ThreadLocalMap变量属于线程的内部属性,不同的线程拥有完全不同的ThreadLocalMap变量
2.线程中的ThreadLocalMap变量的值是在ThreadLocal对象进行set或者get操作时 创建的
3.在创建ThreadLocalMap之前,会首先检查当前线程中的ThreadLocalMap变量是 否已经存在,如果不存在则创建一个;如果已经存在,则使用当前线程已创建的ThreadLocalMap
4.使用当前线程的ThreadLocalMap的关键在于使用当前的ThreadLocal的实例作为Key进行存储
2、ThreadLocal模式至少从两个方面完成了数据访问隔离,即横向隔离和纵向隔离:
纵向隔离:线程与线程之间的数据访问隔离。这一点由线程的数据结构保证。因为每个线程在进行对象访问时,访问的都是各个线程自己的ThreadLocalMap
横向隔离:同一个线程中,不同的ThreadLocal实例操作的对象之间相互隔离。这一点由ThreadLocalMap在存储时采用当前ThreadLocal的实例作为key来保证。
3、比较ThreadLocal模式与synchronized关键字
1.ThreadLocal是一个Java类,通过对当前线程中的局部变量的操作来解决不同线程的变量访问的冲突问题。所以,ThreadLocal提供了线程安全的共享对象机制,每个线程都拥有其副本。
2.java中的synchronized是一个保留字,它依靠JVM的锁机制来实现临界区的函数或者变量在访问中的原子性。在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。此时,被用作“锁机制”的变量是多个线程共享的
同步机制采用了“以时间换空间”的方式,提供了一份变量,让不同的线程排队访问。而ThreadLocal采用了“以空间换时间”的方式,为每一个线程都提供了一分变量的副本,从而实现同时访问而互补影响。
三、 ThreadLocal模式的核心元素
1、ThreadLocal模式最合适的使用场景:在同一个线程的不同开发层次中共享数据
2、实现ThreadLocal模式的两个主要步骤:
1.建立一个类,并在其中封装一个静态的ThreadaLocal变量,使其成为一个共享数据环 境
2.在类中实现访问静态ThreadLocal变量的静态方法(设值和取值)
3、使用ThreadLocal模式,可以使数据在不同的编程层次得到有效共享