几乎每种操作系统都支持进程的概念 ―― 进程就是在某种程度上相互隔离的、独立运行的程序。
线程化是允许多个活动共存于一个进程中的工具。大多数现代的操作系统都支持线程,而且线程的概念以各种形式已存在了好多年。Java
是第一个在语言本身中显式地包含线程的主流编程语言,它没有把线程化看作是底层操作系统的工具。
有时候,线程也称作轻量级进程。就象进程一样,线程在程序中是独立的、并发的执行路径,每个线程有它自己的堆栈、自己的程序计数器和自己的局部变量。但是,与分隔的进程相比,进程中的线程之间的隔离程度要小。它们共享内存、文件句柄和其它每个进程应有的状态。
进程可以支持多个线程,它们看似同时执行,但互相之间并不同步。一个进程中的多个线程共享相同的内存地址空间,这就意味着它们可以访问相同的变量和对象,而且它们从同一堆中分配对象。尽管这让线程之间共享信息变得更容易,但您必须小心,确保它们不会妨碍同一进程里的其?叱獭?
Java 线程工具和 API
看似简单。但是,编写有效使用线程的复杂程序并不十分容易。因为有多个线程共存在相同的内存空间中并共享相同的变量,所以您必须小心,确保您的线程不会互相干扰。
每个 Java 程序都使用线程
每个 Java 程序都至少有一个线程 ― 主线程。当一个 Java 程序启动时,JVM 会创建主线程,并在该线程中调用程序的 main()
方法。
JVM 还创建了其它线程,您通常都看不到它们 ― 例如,与垃圾收集、对象终止和其它 JVM 内务处理任务相关的线程。其它工具也创建线程,如
AWT(抽象窗口工具箱(Abstract Windowing Toolkit))或 Swing UI 工具箱、servlet 容器、应用程序服务器和
RMI(远程方法调用(Remote Method Invocation))。
为什么使用线程?
在 Java 程序中使用线程有许多原因。如果您使用 Swing、servlet、RMI 或 Enterprise
JavaBeans(EJB)技术,您也许没有意识到您已经在使用线程了。
使用线程的一些原因是它们可以帮助:
- 使 UI 响应更快
- 利用多处理器系统
- 简化建模
- 执行异步或后台处理
事件驱动的 UI 工具箱(如 AWT 和 Swing)有一个事件线程,它处理 UI 事件,如击键或鼠标点击。
AWT 和 Swing 程序把事件侦听器与 UI 对象连接。当特定事件(如单击了某个按钮)发生时,这些侦听器会得到通知。事件侦听器是在 AWT
事件线程中调用的。
如果事件侦听器要执行持续很久的任务,如检查一个大文档中的拼写,事件线程将忙于运行拼写检查器,所以在完成事件侦听器之前,就不能处理额外的 UI
事件。这就会使程序看来似乎停滞了,让用户不知所措。
要避免使 UI 延迟响应,事件侦听器应该把较长的任务放到另一个线程中,这样 AWT 线程在任务的执行过程中就可以继续处理 UI
事件(包括取消正在执行的长时间运行任务的请求)。
利用多处理器系统
多处理器(MP)系统比过去更普及了。以前只能在大型数据中心和科学计算设施中才能找到它们。现在许多低端服务器系统 ― 甚至是一些台式机系统 ―
都有多个处理器。
现代操作系统,包括 Linux、Solaris 和 Windows NT/2000,都可以利用多个处理器并调度线程在任何可用的处理器上执行。
调度的基本单位通常是线程;如果某个程序只有一个活动的线程,它一次只能在一个处理器上运行。如果某个程序有多个活动线程,那么可以同时调度多个线程。在精心设计的程序中,使用多个线程可以提高程序吞吐量和性能。
简化建模
在某些情况下,使用线程可以使程序编写和维护起来更简单。考虑一个仿真应用程序,您要在其中模拟多个实体之间的交互作用。给每个实体一个自己的线程可以使许多仿真和对应用程序的建模大大简化。
另一个适合使用单独线程来简化程序的示例是在一个应用程序有多个独立的事件驱动的组件的时候。例如,一个应用程序可能有这样一个组件,该组件在某个事件之后用秒数倒计时,并更新屏幕显示。与其让一个主循环定期检查时间并更新显示,不如让一个线程什么也不做,一直休眠,?钡侥骋欢问奔浜螅缕聊簧系募剖鳎庋虻ィ也蝗菀壮龃怼U庋飨叱叹透疚扌璧P募剖逼鳌?/P>
异步或后台处理
服务器应用程序从远程来源(如套接字)获取输入。当读取套接字时,如果当前没有可用数据,那么对 SocketInputStream.read()
的调用将会阻塞,直到有可用数据为止。
如果单线程程序要读取套接字,而套接字另一端的实体并未发送任何数据,那么该程序只会永远等待,而不执行其它处理。相反,程序可以轮询套接字,查看是否有可用数据,但通常不会使用这种做法,因为会影响性能。
但是,如果您创建了一个线程来读取套接字,那么当这个线程等待套接字中的输入时,主线程就可以执行其它任务。您甚至可以创建多个线程,这样就可以同时读取多个套接字。这样,当有可用数据时,您会迅速得到通知(因为正在等待的线程被唤醒),而不必经常轮询以检查是否有可?檬荨J褂孟叱痰却捉幼值拇胍脖嚷盅虻ァ⒏灰壮龃怼?/P>
虽然 Java 线程工具非常易于使用,但当您创建多线程程序时,应该尽量避免一些风险。
当多个线程访问同一数据项(如静态字段、可全局访问对象的实例字段或共享集合)时,需要确保它们协调了对数据的访问,这样它们都可以看到数据的一致视图,而且相互不会干扰另一方的更改。为了实现这个目的,Java
语言提供了两个关键字:synchronized
和
volatile
。我们将稍后在本教程中研究这些关键字的用途和意义。
当从多个线程中访问变量时,必须确保对该访问正确地进行了同步。对于简单变量,将变量声明成 volatile
也许就足够了,但在大多数情况下,需要使用同步。
如果您将要使用同步来保护对共享变量的访问,那么必须确保在程序中所有访问该变量的地方都使用同步。
不要做过头
虽然线程可以大大简化许多类型的应用程序,过度使用线程可能会危及程序的性能及其可维护性。线程消耗了资源。因此,在不降低性能的情况下,可以创建的线程的数量是有限制的。
尤其在单处理器系统中,使用多个线程不会使主要消耗 CPU 资源的程序运行得更快。
示例:使用一个线程用于计时,并使用另一个线程完成工作
以下示例使用两个线程,一个用于计时,一个用于执行实际工作。主线程使用非常简单的算法计算素数。
在它启动之前,它创建并启动一个计时器线程,这个线程会休眠十秒钟,然后设置一个主线程要检查的标志。十秒钟之后,主线程将停止。请注意,共享标志被声明成
volatile
。
/**
* CalculatePrimes -- calculate as many primes as we can in ten seconds
*/
public class CalculatePrimes extends Thread {
public static final int MAX_PRIMES = 1000000;
public static final int TEN_SECONDS = 10000;
public volatile boolean finished = false;
public void run() {
int[] primes = new int[MAX_PRIMES];
int count = 0;
for (int i=2; count<MAX_PRIMES; i++) {
// Check to see if the timer has expired
if (finished) {
break;
}
boolean prime = true;
for (int j=0; j<count; j++) {
if (i % primes[j] == 0) {
prime = false;
break;
}
}
if (prime) {
primes[count++] = i;
System.out.println("Found prime: " + i);
}
}
}
public static void main(String[] args) {
CalculatePrimes calculator = new CalculatePrimes();
calculator.start();
try {
Thread.sleep(TEN_SECONDS);
}
catch (InterruptedException e) {
// fall through
}
calculator.finished = true;
}
}
小结
Java 语言包含了内置在语言中的功能强大的线程工具。您可以将线程工具用于:
增加 GUI 应用程序的响应速度
利用多处理器系统
当程序有多个独立实体时,简化程序逻辑
在不阻塞整个程序的情况下,执行阻塞 I/O
当使用多个线程时,必须谨慎,遵循在线程之间共享数据的规则,我们将在共享对数据的访问中讨论这些规则。所有这些规则归结为一条基本原则:不要忘了同步。
分享到:
相关推荐
Java线程是多任务编程中的核心概念,它允许程序同时执行多个不同的任务,极大地提高了程序的效率和响应性。在Java中,线程是程序执行的最小单元,每个线程都有自己的程序计数器、虚拟机栈、本地方法栈和一部分堆内存...
本教程研究了线程的基础知识 — 线程是什么、线程为什么有用以及怎么开始编写使用线程的简单程序。 <br>我们还将研究更复杂的、使用线程的应用程序的基本构件 — 如何在线程之间交换数据、如何控制线程以及线程...
第一章 线程简介 第二章 Java线程API 第三章 同步技术 第四章 等待和通知 第五章 Java线程编程的例子 第六章 Java线程调度 第七章 Java线程调度例子 第八章 和同步相关的高级主题 第九章 多处理器机器上的并行化 ...
java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具...
本教程适用于拥有丰富 Java 语言应用知识,但又没有多少多线程或并发性经验的 Java 程序员。 学习完本教程之后,您应该可以编写一个使用线程的简单程序。您还应该可以阅读并理解以简单方法使用线程的程序。
根据提供的信息,我们可以推断出这份文档主要关注的是Java线程的相关内容。下面将围绕“Java线程”这一主题展开详细的介绍与解释。 ### Java线程基础 在Java语言中,线程是程序执行流的基本单元。一个标准的Java...
Java线程状态流转图知识点总结 Java线程状态流转图是一种用于描述Java线程生命周期中不同的状态和状态转换的图形表示方式。该图形展示了Java线程从创建到终止的整个生命周期,并详细介绍了每种状态的特点和转换...
#### 一、Java线程简介 Java中的线程是一个轻量级的进程,它能够在一个程序中并发地执行多个任务。Java语言直接支持线程的概念,这使得Java成为多线程编程的理想选择。与进程相比,线程具有以下优势: - **创建...
Java线程是并发编程的核心部分,它允许程序在同一时间执行多个独立的任务,从而提高系统效率和响应速度。本文将深入探讨Java线程的概念、生命周期、实现方式以及相关的同步机制。 首先,理解线程的基本概念至关重要...
java 线程Dump 分析工具: Java的TDA线程转储分析器是一个用于分析Sun Java VM生成的线程转储和堆信息的小型Swing GUI(目前用1.4测试)。它从提供的日志文件中解析线程转储和类直方图。它提供关于发现的线程转储的...
#### 一、Java线程简介 Java中的线程机制是其一大亮点之一,它直接支持线程级别的并发处理。线程相比于进程具有创建成本低、上下文切换快等优势,这使得Java在处理高并发场景时能够表现出色。然而,线程的使用对...
Java线程是Java编程中的重要概念,特别是在多核处理器和并发处理中不可或缺。Java线程允许程序在同一时间执行多个不同的任务,从而提高了程序的效率和响应性。在燕山大学信息学院计算机系的课程中,李峰教授讲解了...
《Java线程(第三版)》是一本深入探讨Java线程技术的专业书籍,旨在帮助开发者理解和掌握Java平台上的多线程编程。Java线程是并发编程的重要组成部分,它允许程序同时执行多个任务,从而充分利用系统资源,提高程序的...
Java线程分析是Java开发中的重要环节,尤其是在处理性能优化、死锁排查或者并发问题时。TDA(Thread Dump Analyzer)是一款强大的Java线程分析工具,它能够帮助开发者深入理解应用在运行时的线程状态,包括线程的...
Java线程有10个优先级(MIN_PRIORITY, NORM_PRIORITY, MAX_PRIORITY),默认优先级是NORM_PRIORITY。但是,线程优先级并不保证绝对的执行顺序,操作系统调度策略可能影响实际执行顺序。 7. join()方法: 一个线程...
java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类....
JAVA线程dump的分析 JAVA线程dump是指在JAVA程序中,当前线程的状态和调用堆栈的快照,能够帮助开发者了解当前程序的执行情况,诊断问题和性能瓶颈。生成JAVA线程dump的方法在不同的操作系统下是不同的,在Windows...
Java线程是Java编程语言中的核心概念,尤其在多任务处理和并发编程中扮演着重要角色。线程允许一个程序内部同时执行多个独立的控制流,使得程序能够更高效地利用处理器资源。本文将深入解析Java线程的相关知识点,...
Java线程是Java编程语言中的一个核心概念,它允许程序同时执行多个任务,极大地提高了程序的并发性和效率。本教程将深入探讨Java线程的使用,帮助开发者掌握这一关键技术。 一、线程基础 1. **线程的概念**:线程...
Java线程是多任务编程的重要概念,它允许程序同时执行多个独立的任务,从而提高系统效率和响应速度。在Java中,线程可以分为用户线程和守护线程,前者是程序运行的基础,而后者是在所有用户线程结束时才终止的后台...