`
y806839048
  • 浏览: 1126237 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

多线程

阅读更多
ThreadLocal 变量
早在JDK 1.2 的版本中就提供java.lang.ThreadLocal,为解决多线程程序的并发问题提供
了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。
ThreadLocal 很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal
并不是一个Thread,而是Thread 的局部变量,也许把它命名为ThreadLocalVariable 更容易让人理解一些。当使用ThreadLocal 维护变量时,ThreadLocal 为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
从线程的角度看,目标变量就是线程的本地变量,这也是类名中“Local”所要表达的意
思。线程局部变量并不是Java 的新发明,很多语言(如IBM XL FORTRAN)在语法层面就
提供线程局部变量。在Java 中没有提供语言级支持,而是变相地通过ThreadLocal 的类提供
支持。
JDK 5 以后提供了泛型支持,ThreadLocal 被定义为支持泛型:
public class ThreadLocal<T> extends Object
T 为线程局部变量的类型。该类定义了4 个方法:
1) protected T initialValue():返回此线程局部变量的当前线程的“初始值”。线程第一次使用 get() 方法访问变量时将调用此方法,但如果线程之前调用了 set(T) 方法,则不会对该线程再调用 initialValue 方法。通常,此方法对每个线程最多调用一次,但如果在调用 get()后又调用了 remove(),则可能再次调用此方法。
该实现返回 null;如果程序员希望线程局部变量具有 null 以外的值,则必须为
ThreadLocal 创建子类,并重写此方法。通常将使用匿名内部类完成此操作。
2)public T get():返回此线程局部变量的当前线程副本中的值。如果变量没有用于当前
线程的值,则先将其初始化为调用 initialValue() 方法返回的值。
3)public void set(T value):将此线程局部变量的当前线程副本中的值设置为指定值。大部分子类不需要重写此方法,它们只依靠 initialValue() 方法来设置线程局部变量的值。
4)public void remove():移除此线程局部变量当前线程的值。如果此线程局部变量随后
被当前线程读取,且这期间当前线程没有设置其值,则将调用其 initialValue() 方法重新初始化其值。这将导致在当前线程多次调用 initialValue 方法。
下面是一个使用ThreadLocal 的例子,每个线程产生自己独立的序列号。就是使用ThreadLocal 存储每个线程独立的序列号复本,线程之间互不干扰。
package sync;
public class SequenceNumber {
// 定义匿名子类创建ThreadLocal的变量
private static ThreadLocal<Integer> seqNum = new
ThreadLocal<Integer>() {
// 覆盖初始化方法
public Integer initialValue() {
return 0;
}
};
// 下一个序列号
public int getNextNum() {
seqNum.set(seqNum.get() + 1);
return seqNum.get();
}
private static class TestClient extends Thread {
private SequenceNumber sn;
public TestClient(SequenceNumber sn) {
this.sn = sn;
}
// 线程产生序列号
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("thread[" +
Thread.currentThread().getName()
+ "] sn[" + sn.getNextNum() + "]");
}
}
}
/**
* @param args
*/
public static void main(String[] args) {
SequenceNumber sn = new SequenceNumber();
// 三个线程产生各自的序列号
TestClient t1 = new TestClient(sn);
TestClient t2 = new TestClient(sn);
TestClient t3 = new TestClient(sn);
t1.start();
t2.start();
t3.start();
}
}
程序的运行结果如下:
thread[Thread-1] sn[1]
thread[Thread-1] sn[2]
thread[Thread-1] sn[3]
thread[Thread-2] sn[1]
thread[Thread-2] sn[2]
thread[Thread-2] sn[3]
thread[Thread-0] sn[1]
thread[Thread-0] sn[2]
thread[Thread-0] sn[3]
从运行结果可以看出,使用了ThreadLocal 后,每个线程产生了独立的序列号,没有相互干扰。通常我们通过匿名内部类的方式定义ThreadLocal 的子类,提供初始的变量值。ThreadLocal 和线程同步机制相比有什么优势呢?ThreadLocal 和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。
在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是
多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需
要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
而ThreadLocal 则从另一个角度来解决多线程的并发访问。ThreadLocal 会为每一个线程
提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有
自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal 提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,
而ThreadLocal 采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队
访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
需要注意的是ThreadLocal 对象是一个本质上存在风险的工具,应该在完全理解将要使
用的线程模型之后,再去使用ThreadLocal 对象。这就引出了线程池(thread pooling)的问题,线程池是一种线程重用技术,有了线程池就不必为每个任务创建新的线程,一个线程可能会多次使用,用于这种环境的任何ThreadLocal 对象包含的都是最后使用该线程的代码所设置的状态,而不是在开始执行新线程时所具有的未被初始化的状态。
那么ThreadLocal 是如何实现为每个线程保存独立的变量的副本的呢?通过查看它的源
代码,我们会发现,是通过把当前“线程对象”当作键,变量作为值存储在一个Map 中。
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}

分享到:
评论

相关推荐

    c++多线程

    一个进程中可以同时运行多个线程,每个线程可以执行不同的任务,这就是所谓的多线程。同一进程中的多个线程将共享该进程中的全部系统资源,如虚拟地址空间、文件描述符和信号处理等,但是同一个进程中的多个线程都有...

    stm32单片机多线程实例

    多线程是实现复杂任务并发执行的关键技术,能够提高资源利用率,优化系统响应时间。在STM32上实现多线程,通常会借助实时操作系统(RTOS)如RT-Thread。 RT-Thread是一个轻量级、开源的实时操作系统,它为STM32等微...

    多线程测试

    在计算机科学领域,多线程是一种程序设计技术,它允许应用程序同时执行多个任务或子任务。这极大地提高了软件的效率和响应性,特别是在现代多核处理器的环境下。本主题将深入探讨多线程的实现方式及其与单线程的对比...

    大漠多线程模板_大漠_大漠多线程_

    "大漠多线程模板"是一个专门针对C#开发的多线程处理框架,它为开发者提供了便捷的方式来管理和优化多线程应用。这个框架由知名开发者"大漠"创建,旨在简化复杂的并发编程,提高代码的可读性和可维护性。 多线程允许...

    安卓多线程

    安卓多线程,安卓高级开发中,必备技能,项目开发中都要使用

    qt QTcpServer多线程

    QTcpServer多线程 每个客户端连接的tcpSocket分别分配一个专门的线程来处理。 核心思想:继承并重写QTcpServer的incomingConnection函数去自己实现tcpsocket连接的建立和分配。 incomingConnection函数说明: 当...

    多线程介绍

    多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多...

    多线程基础与基于多线程的简单聊天室

    在IT行业中,多线程是程序设计中的一个重要概念,尤其在Java编程中,它被广泛应用于提高应用程序的并发性能和响应速度。本压缩包“多线程基础与基于多线程的简单聊天室”提供了对多线程技术的实践理解和二次开发的...

    多线程示例

    在IT领域,多线程是程序设计中的一个重要概念,尤其在现代计算机系统中,它能够提升应用程序的效率和响应性。MFC(Microsoft Foundation Classes)是微软提供的一个C++类库,用于简化Windows应用程序的开发,包括...

    qt websocket 客户端 多线程使用

    qt 使用QWebSocket 创建websocket客户端来读取数据,异步链接,并且放入到线程中去执行,线程池的基础,代码使用两个用户,放入到一个线程中执行,同理,可以多个用户放入到多个线程中执行,为线程池执行websocket ...

    java深入理解多线程

    Java多线程是并发编程中的一个重要概念,它允许程序在同一时刻执行多个任务。以下是对Java多线程的深入理解: 线程概述 基本概念:线程是操作系统能够进行运算调度的最小单位,一个进程可以包含多个线程。 特性:...

    WPF多线程实例

    在Windows Presentation Foundation(WPF)开发中,多线程是一个重要的技术,特别是在处理大量数据或进行耗时操作时,为了保持用户界面(UI)的响应性,通常会使用多线程来实现非UI任务。本实例是关于如何在WPF应用...

    多线程导入excel 数据

    在Java编程中,多线程导入Excel数据是一项常见的任务,特别是在大数据处理和高并发场景下。这个场景通常涉及到性能优化和资源管理,以确保系统稳定性和数据一致性。下面将详细阐述多线程导入Excel数据的核心知识点。...

    python多线程测试代码

    使用一个简单的数字递减案例,来模拟多线程下的工作逻辑

    易语言关闭多线程句柄方法

    易语言关闭多线程句柄方法 易语言是一种功能强大且灵活的编程语言,多线程编程是其重要特性之一。然而,在多线程编程中,如何正确地关闭线程句柄是非常重要的。今天,我们将分享易语言关闭多线程句柄方法的相关知识...

    Java多线程详解(超详细)_狂神说笔记完整版_项目代码_适合小白随课程学习

    Java多线程详解 在Java编程中,多线程是一种重要的技术,它使得程序能够同时执行多个任务,提高系统的效率和响应性。本教程将详细讲解Java中的多线程概念,包括线程的创建、状态、同步以及高级主题,旨在帮助初学者...

    Java多线程知识点总结

    Java多线程是Java编程语言中一个非常重要的概念,它允许开发者在一个程序中创建多个执行线程并行运行,以提高程序的执行效率和响应速度。在Java中,线程的生命周期包含五个基本状态,分别是新建状态(New)、就绪...

    详解易语言启动多线程

    易语言多线程启动详解 易语言多线程启动是指在易语言中使用多线程相关的API、支持库或模块来实现多线程编程的技术。多线程编程可以极大地提高程序的执行效率和响应速度,特别是在需要执行大量计算或I/O操作的场景下...

    多线程多线程.xmind

    该文档是笔者在学习李刚老师《Java疯狂讲义》中有关多线程的用法而总结出来的笔记,其中主要的内容包括线程创建和启动、线程的生命周期、控制线程、线程同步、线程通信线程池等基本内容。对Java多线程有详细的介绍。

    C#多线程开发之并发编程经典实例.zip

    在C#编程中,多线程是一个至关重要的概念,尤其对于开发高性能、高并发的应用程序而言。本资源“C#多线程开发之并发编程经典实例”提供了丰富的实例,旨在帮助C#开发者深入理解并掌握多线程技术。以下是关于C#多线程...

Global site tag (gtag.js) - Google Analytics