最近公司在进行Java开发人员的招聘活动,其中有一道面试题是这样的:“请简单描述一下ThreadLocal类的作用。” 结果发现有很多的面试者没有听说过ThreadLocal或者听说过却不知道这个类究竟是用来做什么的。 因此这里写一篇博客来介绍一下ThreadLocal这个类。
在我们日常的项目开发中,ThreadLocal并不是一个经常使用的类。它更多的是被用在诸如Spring,Tomcat或者是Hibernate这些封装了多线程并发的框架或是容器中。而它的目的也正是为了解决多线程并发访问共享数据的问题。
尽管普通开发人员很少有机会涉及到它,了解ThreadLocal也依然有助于他们来学习Java并发编程。通过阅读ThreadLocal的源码并了解它解决并发问题的思路,开发人员可以更好的理解代码中遇到的多线程bug,更不用提那些在项目开发中需要用到多线程编程的开发人员了。因此,不论你是否用到了ThreadLocal类,都很有必要学习一下它。
在我们讨论代码细节之前,先来看看java concurrent in practice中对于多线程并发问题的描述:
“所有的多线程问题都可以归结为多个线程访问共享的可变状态时的管理问题。”
这里的状态也就我们说的数据。这句话说明多线程问题必须在以下三个条件都满足的时候才会发生:
1. 拥有多个线程
2. 共享状态
3. 该状态可变
如果其中任何一个条件没有办法满足,都不会出现多线程问题:
1. 只有单一的线程。 很显然,这并没有多线程问题。
2. 共享状态不可变。 假设某条数据被多线程共享,然而该数据是不可变数据,那么它便没有多线程问题。举例来说: Java中的String类型就是不可变的,因此String的共享并不会导致多线程安全问题。
3. 多线程不共享状态。 任意的数据都由某个线程独占,不与其他线程分享,因此也不会出现多线程问题。
那么相应的,解决多线程问题的办法有以下几种:
1. 在访问状态变量时使用同步。这是最基本的想法,任何一本Java多线程编程的书都会详细描述如何在Java中使用同步,这里不再赘述。
2. 将状态变量修改为不可变的变量。许多新的编程语言,诸如Scala,便是采用这样的办法来解决多线程问题的。
3. 避免线程之间共享状态变量。 我们今天讨论的ThreadLocal,便是属于此类解决办法。
刚刚接触ThreadLocal的同学经常会问这样一个问题:“ThreadLocal是线程安全的么?” 这个问题很难回答,因为当你问这个问题的时候,便默认的认为ThreadLocal是为了解决多线程之间共享状态的访问问题的。虽然ThreadLocal的目的正是如此,但是它所采用的办法是“避免多线程之间共享状态”。既然没有了多线程的共享状态,也就无所谓是否线程安全了。因此不能简单的说ThreadLocal是否线程安全,这个问题其实没有意义。
那么ThreadLocal是如何做到“避免多线程之间的状态共享”的呢?通过在内部维护一个(当前线程 ->对象)的映射表,每个线程都只能访问到映射到自己线程的对象,而无法访问其它线程的对象。通过这种方法,避免了多线程之间的状态共享,自然也就无所谓线程安全问题了。
如果你将某个对象的引用扩散到多个线程中,并将其设置到ThreadLocal里,那么多个线程所指向的便是同一个对象,对它的访问当然也是有线程安全问题的。从这个角度来讲,ThreadLocal并不是线程安全的。
换一个角度来描述: TheadLocal并没有真正解决多线程共享状态的安全问题,它只是通过避免状态共享的办法规避了多线程安全问题。
我们来看一下ThreadLocal的源码(JDK1.6):
/** * 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(); }
可以看到,当ThreadLocal的get方法被调用时,首先利用当前线程作为key获得了一个map,而这个map便是当前线程专属的,其它线程无法访问。在从该Map中找到相应的对象并返回。
而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); }
我们来看一个在Hibernate中使用ThreadLocal的例子:
private static ThreadLocal<Connection> connectionHolder = new ThreadLoca<Connection>() { public Connection initialValue() { return DriverManager.getConnection(DB_URL); } }; public static Connection getConnection() { return ConnectionHolder.get(); }
上面的例子是一个最经典的ThreadLocal使用案例: 在单线程中创建一个单例变量,并在程序启动时初始化该单例变量,从而避免在调用每个方法时都要传递该变量。然而该单例可能并不是线程安全的,因此,当多线程应用程序在没有互相协作的情况下,可以通过将该单例变量保存到ThreadLocal中,以确保每一个线程都拥有属于自己的实例。
在这里,一个更好理解的说法便是:该单例是一个线程内单例,在多线程应用中,每个线程里都有一个该单例。
通过学习ThreadLocal,我们能够对正确的在项目中使用它,同时,也能够帮助我们对多线程编程有一个更深的认识.
相关推荐
**线程局部变量(ThreadLocal)是Java编程中一个非常重要的工具类,它在多线程环境下提供了线程安全的数据存储。ThreadLocal并不是一个变量,而是一个类,它为每个线程都创建了一个独立的变量副本,使得每个线程都...
ThreadLocal是Java编程语言中的一个类,用于在多线程环境中提供线程局部变量。它是一种特殊类型的变量,每个线程都有自己的副本,互不影响,从而实现线程间数据隔离。ThreadLocal通常被用来解决线程共享数据时可能...
在 `LeakingServlet` 的 `doGet` 方法中,如果 `ThreadLocal` 没有设置值,那么会创建一个新的 `MyCounter` 并设置到 `ThreadLocal` 中。关键在于,一旦 `MyCounter` 被设置到 `ThreadLocal`,那么它将与当前线程...
### 正确理解ThreadLocal:深入解析其工作原理与应用场景 #### 一、ThreadLocal的基本概念 `ThreadLocal`是Java平台提供的一种线程局部变量的解决方案,它为每一个使用该变量的线程都提供了独立的变量副本,使得每...
Java中的ThreadLocal是一个非常重要的工具类,它在多线程编程中扮演着独特角色,尤其在处理线程间数据隔离和共享时。ThreadLocal不是线程本身,而是为每个线程提供一个独立的变量副本,使得每个线程都可以独立地改变...
ThreadLocal 整理 ThreadLocal 是 Java 中的一个重要组件,它能够在每个线程中保持独立的副本。这个功能是通过 Thread 类中的 threadLocals 属性来实现的,这个属性实际上是一个 Entry 数组,其中的每个 Entry 都...
Java事务和ThreadLocal是两种在Java编程中至关重要的概念,它们分别用于处理多线程环境下的数据一致性问题和提供线程局部变量。 首先,我们来深入理解Java事务。在数据库操作中,事务是一系列操作的集合,这些操作...
理解ThreadLocal 理解ThreadLocal 理解ThreadLocal 理解ThreadLocal
### Java中ThreadLocal详解 #### 一、ThreadLocal概述 在Java多线程编程中,`ThreadLocal`是一个非常重要的工具类,它提供了一种在每个线程内部存储线程私有实例的方法。通常情况下,当多个线程共享某个变量时,...
ThreadLocal是Java编程中一种非常特殊的变量类型,它主要用于在多线程环境下为每个线程提供独立的变量副本,从而避免了线程间的数据共享和冲突。然而,ThreadLocal在理解和使用过程中容易产生一些误区,这里我们将...
**线程局部变量(ThreadLocal)** 在Java编程中,`ThreadLocal`是一个非常重要的工具类,它用于在多线程环境中提供线程安全的局部变量。`ThreadLocal`并不是一个线程,而是一个线程局部变量的容器,每个线程都有自己...
**线程局部变量(ThreadLocal)是Java编程中一个非常重要的概念,主要用于在多线程环境中为每个线程提供独立的变量副本。ThreadLocal不是一种数据结构,而是一种解决线程间共享数据的方式,它提供了线程安全的局部...
本资料主要聚焦于两种设计模式以及Java中的ThreadLocal特性。 首先,我们来探讨单例模式。单例模式是一种确保一个类只有一个实例,并提供全局访问点的设计模式。在Java中,通常通过私有构造函数、静态工厂方法或...
**ThreadLocal概述** ThreadLocal是Java中的一个线程局部变量类,它为每个线程创建了一个独立的变量副本。这意味着每个线程都有自己的ThreadLocal变量,互不干扰,提供了线程安全的数据存储方式。ThreadLocal通常...
在Java编程中,ThreadLocal是线程局部变量的类,它提供了一种在多线程环境中为每个线程创建和维护独立副本的机制。ThreadLocal主要用于解决线程间的数据隔离问题,确保各线程拥有自己的变量副本,避免了数据共享带来...
ThreadLocal是Java编程语言中的一个线程局部变量类,它为每个线程提供了一个独立的变量副本,使得每个线程可以独立地改变自己的副本,而不会影响其他线程所对应的副本。这个特性在多线程环境下处理并发问题时非常...
private static final ThreadLocal<Connection> connectionHolder = new ThreadLocal(); public static void setConnection(Connection conn) { connectionHolder.set(conn); } public static Connection get...