`
newleague
  • 浏览: 1501097 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类

研究java.lang.ThreadLocal类(一)

阅读更多

一、概述
ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。
从线程的角度看,每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。
通过ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM 为每个运行的线程,绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发访问问题提供了一种隔离机制。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
二、API说明
ThreadLocal()
          创建一个线程本地变量。
T get()
          返回此线程局部变量的当前线程副本中的值,如果这是线程第一次调用该方法,则创建并初始化此副本。
protected T initialValue()
          返回此线程局部变量的当前线程的初始值。最多在每次访问线程来获得每个线程局部变量时调用此方法一次,即线程第一次使用 get() 方法访问变量的时候。如果线程先于 get 方法调用 set(T) 方法,则不会在线程中再调用 initialValue 方法。
   若该实现只返回 null;如果程序员希望将线程局部变量初始化为 null 以外的某个值,则必须为 ThreadLocal 创建子类,并重写此方法。通常,将使用匿名内部类。initialValue 的典型实现将调用一个适当的构造方法,并返回新构造的对象。
void remove()
          移除此线程局部变量的值。这可能有助于减少线程局部变量的存储需求。如果再次访问此线程局部变量,那么在默认情况下它将拥有其 initialValue。
void set(T value)
          将此线程局部变量的当前线程副本中的值设置为指定值。许多应用程序不需要这项功能,它们只依赖于 initialValue() 方法来设置线程局部变量的值。
在程序中一般都重写initialValue方法,以给定一个特定的初始值。
三、典型实例
1、Hiberante的Session 工具类HibernateUtil
这个类是Hibernate官方文档中HibernateUtil类,用于session管理。
public class HibernateUtil {
    private static Log log = LogFactory.getLog(HibernateUtil.class);
    private static final SessionFactory sessionFactory;     //定义SessionFactory
    static {
        try {
            // 通过默认配置文件hibernate.cfg.xml创建SessionFactory
            sessionFactory = new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            log.error("初始化SessionFactory失败!", ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    //创建线程局部变量session,用来保存Hibernate的Session
    public static final ThreadLocal session = new ThreadLocal();
    /**
     * 获取当前线程中的Session
     * @return Session
     * @throws HibernateException
     */
    public static Session currentSession() throws HibernateException {
        Session s = (Session) session.get();
        // 如果Session还没有打开,则新开一个Session
        if (s == null) {
            s = sessionFactory.openSession();
            session.set(s);         //将新开的Session保存到线程局部变量中
        }
        return s;
    }
    public static void closeSession() throws HibernateException {
        //获取线程局部变量,并强制转换为Session类型
        Session s = (Session) session.get();
        session.set(null);
        if (s != null)
            s.close();
    }
}
在这个类中,由于没有重写ThreadLocal的initialValue()方法,则首次创建线程局部变量session其初始值为null,第一次调用currentSession()的时候,线程局部变量的get()方法也为null。因此,对session做了判断,如果为null,则新开一个Session,并保存到线程局部变量session中,这一步非常的关键,这也是“public static final ThreadLocal session = new ThreadLocal()”所创建对象session能强制转换为Hibernate Session对象的原因。
2、另外一个实例
创建一个Bean,通过不同的线程对象设置Bean属性,保证各个线程Bean对象的独立性。
/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-11-23
* Time: 10:45:02
* 学生
*/
public class Student {
    private int age = 0;   //年龄
    public int getAge() {
        return this.age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2007-11-23
* Time: 10:53:33
* 多线程下测试程序
*/
public class ThreadLocalDemo implements Runnable {
    //创建线程局部变量studentLocal,在后面你会发现用来保存Student对象
    private final static ThreadLocal studentLocal = new ThreadLocal();
    public static void main(String[] agrs) {
        ThreadLocalDemo td = new ThreadLocalDemo();
        Thread t1 = new Thread(td, "a");
        Thread t2 = new Thread(td, "b");
        t1.start();
        t2.start();
    }
    public void run() {
        accessStudent();
    }
    /**
     * 示例业务方法,用来测试
     */
    public void accessStudent() {
        //获取当前线程的名字
        String currentThreadName = Thread.currentThread().getName();
        System.out.println(currentThreadName + " is running!");
        //产生一个随机数并打印
        Random random = new Random();
        int age = random.nextInt(100);
        System.out.println("thread " + currentThreadName + " set age to:" + age);
        //获取一个Student对象,并将随机数年龄插入到对象属性中
        Student student = getStudent();
        student.setAge(age);
        System.out.println("thread " + currentThreadName + " first read age is:" + student.getAge());
        try {
            Thread.sleep(500);
        }
        catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        System.out.println("thread " + currentThreadName + " second read age is:" + student.getAge());
    }
    protected Student getStudent() {
        //获取本地线程变量并强制转换为Student类型
        Student student = (Student) studentLocal.get();
        //线程首次执行此方法的时候,studentLocal.get()肯定为null
        if (student == null) {
            //创建一个Student对象,并保存到本地线程变量studentLocal中
            student = new Student();
            studentLocal.set(student);
        }
        return student;
    }
}
运行结果:
a is running!
thread a set age to:76
b is running!
thread b set age to:27
thread a first read age is:76
thread b first read age is:27
thread a second read age is:76
thread b second read age is:27
可以看到a、b两个线程age在不同时刻打印的值是完全相同的。这个程序通过妙用ThreadLocal,既实现多线程并发,游兼顾数据的安全性。
四、总结
ThreadLocal使用场合主要解决多线程中数据数据因并发产生不一致问题。ThreadLocal为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存,单大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。
ThreadLocal不能使用原子类型,只能使用Object类型。ThreadLocal的使用比synchronized要简单得多。
ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。
五、ThreadLocal使用的一般步骤
1、在多线程的类(如ThreadDemo类)中,创建一个ThreadLocal对象threadXxx,用来保存线程间需要隔离处理的对象xxx。
2、在ThreadDemo类中,创建一个获取要隔离访问的数据的方法getXxx(),在方法中判断,若ThreadLocal对象为null时候,应该new()一个隔离访问类型的对象,并强制转换为要应用的类型。
3、在ThreadDemo类的run()方法中,通过getXxx()方法获取要操作的数据,这样可以保证每个线程对应一个数据对象,在任何时刻都操作的是这个对象。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/teedry/archive/2010/01/11/5177693.aspx

分享到:
评论

相关推荐

    入研究java.lang.ThreadLocal类.docx

    ### 知识点详解:Java.lang.ThreadLocal 类 #### 一、概述 **ThreadLocal** 并非线程的一种特殊实现形式,而是一种为每个线程提供独立副本的机制,通常被称为“线程局部变量”。这种机制使得每个线程都可以独立...

    java.lang包介绍

    在Java 2(J2SE)中,`java.lang`包添加了一些新的类和方法,如`InheritableThreadLocal`,`ThreadLocal`的扩展,使得子线程可以继承父线程的变量,以及`SecurityManager`,用于实现安全管理策略。 过时方法在Java ...

    java.lang研究

    Java语言的核心库之一就是`java.lang`包,它包含了Java程序设计中最基础的类和接口,这些类和接口是每个Java应用程序必不可少的。由于`java.lang`包在编译时自动导入,因此程序员无需显式引入。本章将详细探讨这个...

    java_lang包详解.pdf

    java.lang包还包含了一些与安全、线程和内存管理相关的类,如SecurityManager用于安全管理,Thread和ThreadGroup是线程相关的类,ClassLoader负责加载类,ThreadLocal为每个线程提供独立的变量副本,而Runtime类提供...

    Can't find a codec for class java.math.BigDecimal.txt

    解决mongo数据插入时 报错问题 mogodb插入数据时报错Can't find a codec for class java.math.BigDecimal

    java-lang包详解

    总结,`java.lang`包是Java的核心,包含了一系列基础类和接口,它们为Java程序提供了基本的运算、数据类型转换以及多线程、安全性等方面的支持。理解和熟练使用这些类和接口是掌握Java编程的关键。

    java-java-lang包.pdf

    Java编程语言中的java.lang包是一个核心包,它提供了Java语言运行时的基础类库,包含了Java程序运行的基本数据类型、异常处理类以及一些用于系统级别的工具类和接口。java.lang包中的类和接口不需要程序员显示地导入...

    threadlocal:java.lang.ThreadLocal 的实验性无等待并发实现

    在Java编程中,`ThreadLocal`是一个非常重要的工具类,它提供了线程局部变量的功能。线程局部变量是局限于单个线程的变量,每个线程都拥有自己独立的副本,互不干扰。这个实验性的实现旨在优化`ThreadLocal`的正确性...

    java_javalang包.pdf

    最后,`Thread`和`ThreadGroup`类构成了Java的多线程机制,`ThreadLocal`则提供线程私有的变量,确保每个线程都有自己的副本,不会互相干扰。 总的来说,`java.lang`包是Java程序的基础,其提供的类和接口构成了...

    Java多线程编程中ThreadLocal类的用法及深入

    早在 JDK 1.2 的时代,java.lang.ThreadLocal 就诞生了,它是为了解决多线程并发问题而设计的,只不过设计得有些难用,所以至今没有得到广泛使用。其实它还是挺有用的,不相信的话,我们一起来看看这个例子吧。 一个...

    Java资料-详解ThreadLocal

    `ThreadLocal`的全称是`java.lang.ThreadLocal`,它并不是一个线程,而是一个线程局部变量的容器。`ThreadLocal`的工作原理是为每个线程创建一个单独的实例副本,这些副本存储在一个与线程相关的映射表中。当线程...

    JAVA API1.6中文文档.CHM

    4. **多线程**:`java.lang.Thread`和`java.util.concurrent`包提供了线程相关类和接口,用于实现并发编程,如`Runnable`、`ThreadLocal`、`ExecutorService`。 5. **网络编程**:`java.net`包包含网络通信所需的...

    threadlocal.rar

    ThreadLocal,全称为`java.lang.ThreadLocal`,是Java中一个非常重要的工具类,它主要用于在多线程环境中提供线程局部变量。ThreadLocal不是用来解决线程安全问题的,而是为每个线程创建独立的变量副本,确保线程间...

    Java API原生文档

    3. **多线程**:Java API支持多线程编程,`java.lang.Thread`类和`java.util.concurrent`包提供了丰富的线程控制和同步机制,如`synchronized`关键字、`ThreadLocal`、`ExecutorService`等。 4. **网络编程**:`...

    oom-diag:java.lang.OutOfMemoryError 的决策树

    Java中的`java.lang.OutOfMemoryError`是开发者经常会遇到的一个严重问题,它表明程序在运行过程中耗尽了可用的内存资源。本知识点将深入探讨如何诊断和解决这类问题,主要聚焦于Java,虽然标签中提到了JavaScript,...

    java 1.8 API 中文版

    3. **并发编程**:`java.util.concurrent`包提供了高效的并发工具,如ExecutorService、Future、Callable、ThreadLocal和各种并发集合(ConcurrentHashMap、CopyOnWriteArrayList等),以及锁和同步工具类。...

    ThreadLocal的用处

    ThreadLocal是Java的`java.lang.ThreadLocal`类,它的主要作用和用法如下: 1. **线程局部变量**: ThreadLocal并不是一个存储数据的容器,而是一个用于创建线程局部变量的模板。当你在ThreadLocal中创建一个实例...

    Java面试要点(适用于2年以上经验,1年亦可)

    Java 是一种广泛使用的编程语言,涵盖了基础知识、JVM、并发、锁、分布式等领域。以下是Java面试要点的详细知识点总结: 基础篇 1. JVM 内存结构:堆、栈、方法区、直接内存、堆和栈区别 2. Java 内存模型:内存...

    java多线程编程教程

    - **Java中的线程**: 在Java中,“线程”可以指两个方面的事物:一个是`java.lang.Thread`类的一个实例,另一个是线程的实际执行过程。 - **线程实例化与启动**: 可以通过实现`java.lang.Thread`类或`java.lang....

Global site tag (gtag.js) - Google Analytics