ThreadLocal的本质和应用分析

引言:  在Java的多线程编程中,竞争资源的同步是一个需要格外关注的问题。处理使用volatile和同步锁机制实现资源访问的一致性之外,还可以使用ThreadLocal来保存线程的私有变量,从而避免了竞争资源的产生。


1.  ThreadLocal是什么?

    ThreadLocal是服务于Thread的一种本地私有数据机制,threadlocalvariable(线程局部变量), 即为每一个使用该变量的线程都提供一个变量值的副本,与使用的线程绑定,每一个线程都可以独立地改变自己的副本,不会产生冲突。

      其在Thread处于活跃状态,并且threadLocal对象可用的情况下,就可以在其中存放数据。在线程被销毁之后,则ThreadLocal中使用的资源和内存同时也被回收。

     本质上,JVM通过ThreadLocal为多线程情况下,进行资源的访问提供了一种隔离机制,简化了编程的复杂度。

2.  ThreadLocal的用法

     2.1 ThreadLocal的主要方法

  •     ThreadLocal()   : 创建实例
  •     T get()  :返回当前线程中的值,如果未设置,则返回初始值
  •     void remove()  : 移除当前线程的值,并释放资源
  •     void set(T value) :设置当前线程线程副本中的
  •     protected void initialValue():  用以在子类中被重写其初始值

      由此可以看出此线程局部变量只可以存放一个对象。


    2.2  ThreadLocal使用示例

      

package org.test;

public class ThreadLocalTest extends Thread {
    //必须的static final
    private final ThreadLocal<Student> mythreadLocal = new ThreadLocal<Student>();
    
    private int age;
    public ThreadLocalTest(String name) {
        super(name);
    }
        
    public ThreadLocalTest(String name, int age) {
        super(name);
        this.age = age;
    }
      
    public static void main(String[] args) {
        ThreadLocalTest student1 = new ThreadLocalTest("thread-first",23);
        ThreadLocalTest student2 = new ThreadLocalTest("thread-second",30);
        
        student1.start();
        student2.start();
    }
    
    public void run() {
        testInfo();
    }
    
    public void testInfo() {
        String currentThreadName = Thread.currentThread().getName();        
        System.out.println(currentThreadName + " is running!");
        
        Student stud1 = this.getLocalVariable();        
        System.out.println("Student " + stud1.name + " is in age " + stud1.age + " in " + currentThreadName);
    }
    
    public Student getLocalVariable() {                  
        if (mythreadLocal.get() == null) {             
            Student student = new Student(Thread.currentThread().getName() + "-student", this.age);
            mythreadLocal.set(student);
        }
        return mythreadLocal.get();
    }
    
    public class Student {
        private String name;
        private int age;        
        public Student(){}
        
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }      
    }
}
  从代码的运行结果来看,他们彼此之间没有什么相互的影响。
Thread-1 is running!
Thread-0 is running!
Student LiSi is in age 30 in Thread-1
Student ZhangSan is in age 24 in Thread-0

  注意:  这里的ThreadLocal按照JavaDoc文档的说明,应该声明为private static,因为它是在多个线程之间共享的一个数据结构,类似Map,以thread对象实例做为Key的。

3.   ThreadLocal和同步锁机制的对比分析

       概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

      同步机制利用所实现资源的同步访问,确保某一个时刻只有一个线程在访问资源;而ThreadLoca则规避了同步,让每一个线程有自己的一份副本。

      他们之间不能彼此替代,只是从不同的角度去解决线程访问资源的问题。threadLocal无法替代锁实现的资源共享,而锁也做不到可以提供给独立的线程实例资源。

4.   基于JVM的内存模型来分析ThreadLocal

      ThreadLocal本质上是一个类似Map的结构,以各个线程对象本身为Key,将其值存放进去。这个结构在所有的基于同一个线程类创建出来的线程中被共享所有,就是只有一个,单例的对象。  虽然,使用get()方法来读取其值,但是默认的是使用当前的thread对象做为Key来检索的。

      具体的实现机制可以参照源码分析。

5.   ThreadLocal的源码分析

       首先,来看看get()方法的源代码

     

   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(); //如果为空,则设置初始值
    }
   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实现。

6.  总结

    ThreadLocal存放的内容,不支持基本数据类型,只支持对象类型。用以存放线程私有的数据,以规避繁琐的同步机制下的资源共享。一般而言,少量数据是可以通过这种简便的方式而实现线程访问的。这些数据不涉及到线程之间的通信和共享问题。

参考文档

1.  http://lavasoft.blog.51cto.com/62575/51926/

2.  http://docs.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ThreadLocalJava中的一个线程本地变量工具类,用于在多线程环境下,为每个线程提供独立的变量副本。它的实现原理是通过在每个线程中维护一个ThreadLocalMap对象,该对象以ThreadLocal实例作为key,以对应的值作为value,存储线程的局部变量。 ThreadLocal应用场景有: 1. 线程安全的对象:如果一个对象在多线程环境中被共享访问,并且它的状态是可变的,那么使用ThreadLocal可以为每个线程提供一个独立的对象副本,避免线程间的竞争和同步问题。 2. 数据库连接管理:在使用数据库连接池时,每个线程需要从连接池中获取数据库连接进行操作,ThreadLocal可以用来存储当前线程所使用的数据库连接,保证每个线程都有自己独立的连接。 3. 事务管理:在多线程环境下,如果某个业务操作需要开启事务,并且事务的隔离级别是线程级别的,则可以使用ThreadLocal来存储当前线程的事务状态,确保每个线程操作的是自己的事务。 4. Web应用中的用户信息存储:在Web应用中,每个请求都创建一个线程进行处理,可以使用ThreadLocal将用户信息保存在当前线程中,方便在不同的业务逻辑中获取用户信息。 5. 并发工具类中的上下文传递:在一些并发框架和工具类中,可能需要将一些上下文信息传递给线程池中的线程,使用ThreadLocal可以将上下文信息绑定到当前线程,方便在线程池中进行访问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值