早在Java 1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序时提供了一种新的选择。使用这个工具类可以很简洁地编写出优美的多线程程序,虽然ThreadLocal非常有用,但是似乎现在了解它、使用它的朋友还不多。
ThreadLocal是什么
ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。线程局部变量并不是Java的新发明,在其它的一些语言编译器实现(如IBM XL FORTRAN)中,它在语言的层次提供了直接的支持。因为Java中没有提供在语言层次的直接支持,而是提供了一个ThreadLocal的类来提供支持,所以,在Java中编写线程局部变量的代码相对比较笨拙,这也许是线程局部变量没有在Java中得到很好的普及的一个原因吧。
ThreadLocal的设计
首先看看ThreadLocal的接口:
Object get() ; // 返回当前线程的线程局部变量副本 protected Object initialValue(); // 返回该线程局部变量的当前线程的初始值void set(Object value); // 设置当前线程的线程局部变量副本的值
ThreadLocal有3个方法,其中值得注意的是initialValue(),该方法是一个protected的方法,显然是为了子类重写而特意实现的。该方法返回当前线程在该线程局部变量的初始值,这个方法是一个延迟调用方法,在一个线程第1次调用get()或者set(Object)时才执行,并且仅执行1次。ThreadLocal中的确实实现直接返回一个null:
protected Object initialValue() { return null; }
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现:
1 public class ThreadLocal
2 {
3 private Map values = Collections.synchronizedMap(new HashMap());
4 public Object get()
5 {
6 Thread curThread = Thread.currentThread();
7 Object o = values.get(curThread);
8 if (o == null && !values.containsKey(curThread))
9 {
10 o = initialValue();
11 values.put(curThread, o);
12 }
13 return o;
14 }
15
16 public void set(Object newValue)
17 {
18 values.put(Thread.currentThread(), newValue);
19 }
20
21 public Object initialValue()
22 {
23 return null;
24 }
25 }
当然,这并不是一个工业强度的实现,但JDK中的ThreadLocal的实现总体思路也类似于此。
ThreadLocal的使用
如果希望线程局部变量初始化其它值,那么需要自己实现ThreadLocal的子类并重写该方法,通常使用一个内部匿名类对ThreadLocal进行子类化,比如下面的例子,SerialNum类为每一个类分配一个序号:
1 public class SerialNum
2 {
3 // The next serial number to be assigned
4
5 private static int nextSerialNum = 0;
6 private static ThreadLocal serialNum = new ThreadLocal()
7 {
8 protected synchronized Object initialValue()
9 {
10 return new Integer(nextSerialNum++);
11 }
12 };
13
14 public static int get()
15 {
16 return ((Integer) (serialNum.get())).intValue();
17 }
18 }
另外我补充一个带有get、set方法的例子,当前类MsgListener是一个线程,这样可以保证其它类在取coreMessage的时候,都取到自己当前线程的coreMessage,不会因为其定义成private static型而被其它的线程把值给改掉,:
SerialNum类的使用将非常地简单,因为get()方法是static的,所以在需要获取当前线程的序号时,简单地调用:
int serial = SerialNum.get();
即可。
在线程是活动的并且ThreadLocal对象是可访问的时,该线程就持有一个到该线程局部变量副本的隐含引用,当该线程运行结束后,该线程拥有的所以线程局部变量的副本都将失效,并等待垃圾收集器收集。
ThreadLocal与其它同步机制的比较
ThreadLocal和其它同步机制相比有什么优势呢?ThreadLocal和其它所有的同步机制都是为了解决多线程中的对同一变量的访问冲突,在普通的同步机制中,是通过对象加锁来实现多个线程对同一变量的安全访问的。这时该变量是多个线程共享的,使用这种同步机制需要很细致地分析在什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放该对象的锁等等很多。所有这些都是因为多个线程共享了资源造成的。ThreadLocal就从另一个角度来解决多线程的并发访问,ThreadLocal会为每一个线程维护一个和该线程绑定的变量的副本,从而隔离了多个线程的数据,每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的整个变量封装进ThreadLocal,或者把该对象的特定于线程的状态封装进ThreadLocal。
由于ThreadLocal中可以持有任何类型的对象,所以使用ThreadLocal get当前线程的值是需要进行强制类型转换。但随着新的Java版本(1.5)将模版的引入,新的支持模版参数的ThreadLocal<T>类将从中受益。也可以减少强制类型转换,并将一些错误检查提前到了编译期,将一定程度地简化ThreadLocal的使用。
总结
当然ThreadLocal并不能替代同步机制,两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;而ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不需要对多个线程进行同步了。所以,如果你需要进行多个线程之间进行通信,则使用同步机制;如果需要隔离多个线程之间的共享冲突,可以使用ThreadLocal,这将极大地简化你的程序,使程序更加易读、简洁。
相关推荐
首先,ThreadLocal类的核心在于为每个线程创建了一个独立的存储空间,这个空间通常以ThreadLocal实例为键,以线程特定的值为值。这样,每个线程都可以有自己的变量副本,互不干扰。ThreadLocal提供了`set`和`get`...
- `java多态.doc`和`java多态.ppt`: 这两份文档可能详细讲解了Java的多态特性,包括接口、抽象类、子类继承父类以及重写方法等概念。 - `HTTPClient用法.rtf`: 可能是HTTPClient的具体使用指南,涵盖了基本用法和...
- 继承`Thread`类:创建一个新的类,继承自`Thread`,重写`run()`方法,然后创建该类的实例并调用`start()`方法启动线程。 - 实现`Runnable`接口:创建一个新的类,实现`Runnable`接口,实现`run()`方法,然后将这...
本实例将详细讲解如何利用Thread类来创建和管理线程。 首先,理解线程的基本概念至关重要。线程是程序执行的最小单元,每个线程都拥有自己的内存空间,可以独立执行代码。在VB中,多线程通常用于执行耗时操作,如...
在本实例源码中,包含17个章节和上百个实例,旨在深入讲解Java多线程的核心概念和实际应用。 一、线程基础知识 在Java中,线程是程序的执行流,每个线程都有自己的程序计数器、虚拟机栈、本地方法栈和一部分堆内存...
5. **多线程**:Java提供了强大的多线程支持,实例将讲解如何创建和管理线程,使用synchronized关键字实现线程同步,以及使用ThreadLocal进行线程局部变量的管理。 6. **IO流**:Java的IO流用于读写文件、网络数据...
ThreadTest.zip文件显然包含了关于Java多线程API的详细讲解和实例代码,重点关注了Thread类、ThreadLocal类以及Executors类。 首先,让我们深入理解Thread类。它是Java中代表独立执行线程的基本类。每个线程都有...
- **`ThreadLocal<T>`类**:提供了线程本地存储的一种实现方式,允许每个线程拥有特定类型的实例。 #### 八、计时器 - **多线程计时器**:这种计时器可以在多个线程中运行,适合于需要并发执行定时任务的场景。 - ...
线程是计算机编程中的一个重要...总结来说,线程threadPPT实例是一个全面讲解线程使用和实践的教程,通过实例帮助开发者熟练掌握线程的创建、管理、同步,以及如何在实际项目中高效、安全地使用线程,提升程序性能。
根据给定的文件信息,文件标题为《Android多线程全新讲解.pdf》,描述为该文档内容的同义表达,标签为“技术及资料”,以及部分内容的概述。在这些信息的基础上,我们可以生成如下知识点: 1. Java多线程基础知识:...
其次,深入讲解了异步编程模型,包括基于回调的异步操作、任务并行库(TPL)和async/await关键字。异步编程能够使程序在等待I/O操作完成时继续执行其他任务,避免阻塞主线程。TPL是.NET Framework提供的高级并行编程...
5. **线程局部存储**:`ThreadLocal<T>`类允许创建每个线程都有独立副本的变量,这对于线程安全的缓存或线程特定的数据存储很有用。 6. **线程优先级**:虽然可以设置线程的优先级,但不推荐过度依赖,因为操作系统...
继承Thread类时,我们需要重写run()方法,而实现Runnable接口则需要实现run()方法并将实例传递给Thread对象。后者更常见,因为它允许更好的代码复用和避免了单继承的限制。 线程的生命周期包括新建(New)、就绪...
本实例65着重讲解了Java线程同步的实现方法,帮助开发者理解和掌握如何在并发环境中保证代码的正确执行。 首先,我们要了解什么是线程。线程是程序中的执行流,每个线程都有自己的程序计数器、栈、局部变量和常量,...
本文将详细讲解Android消息机制的三个关键类:Looper、Handler和Message,以及它们如何协同工作来处理应用程序中的任务。 首先,`Looper`是线程的魔法师,它使得一个普通的线程能够变成一个可以持续循环处理任务的`...
然后通过创建该类的实例并调用start()方法来启动线程。这种方式简单直接,但因为Java不支持多继承,所以如果类已经继承了其他类,就无法再直接继承Thread。 另一种实现多线程的方式是实现Runnable接口。相比于继承...
`ThreadLocal`类提供线程局部变量,每个线程都有自己的副本,不会互相干扰,适合存储线程私有的数据。 以上就是关于Java获取线程ID及相关知识点的详细说明。通过深入理解这些概念和实践,你可以更好地进行多线程...
`ThreadLocal`类则允许在线程范围内创建独立的数据副本,实现线程间的数据隔离。 此外,Java 5引入了**线程池**(`ExecutorService`)的概念,通过`ThreadPoolExecutor`类可以创建和管理线程池,提高系统的效率和可...