第8章 Java的多线程机制
- 格式:pdf
- 大小:218.11 KB
- 文档页数:22
8.2
Thread 类
在 Java 中, 有两种方法可以创建线程: 一种是继承 Thread 类; 另一种是实现 Runnable 接口。但不管采用哪种方式,都要用到 Java 类库中的 Thread 类以及相关方法。
8.2.1
Thread 类的构造方法
Thread 类的构造方法有多个,各有各的用途,如表 8.1 所示。
·269·
Java 开发技术大全
定义用户线程的操作,也就是定义用户线程的 run()方法。 在适当的时候建立用户线程实例,也就是用 new 来创建对象。 启动线程,也就是调用线程对象的 start()方法。 下面通过几个例子分别来介绍这两种方式实现的多线程程序。
8.3.1 利用 Thread 的子类创建线程
第8章
Java 的多线程机制
支持多线程是现代操作系统的一大特点。Java 语言是跨平台的,无法像 C/C++语言一 样通过调用系统的 API 来实现多线程程序,所以它在语言本身加入了对多线程的支持。而 且,所有的多线程功能都是以面向对象的方式来实现,学习起来比较简单,控制也很方便。 本章就将对 Java 的多线程机制做一个简明的介绍。
表 8.1 Thread类的构造方法 构 造 方 法 Thread() Thread(Runnable target) Thread(String name) Thread(ThreadGroup Runnable target) Thread(Runnable target, String name) Thread(ThreadGroup Runnable target, String name) Thread(ThreadGroup stackSize) group, Runnable target, String name, long group, group, 构造一个线程对象 构造一个线程对象,target是被创建线程的目标对象,它实现了 Runnable接口中的run()方法 用指定字符串为名构造一个线程对象 在指定线程组中构造一个线程对象,使用目标对象target的run() 方法 用指定字符串为名构造一个线程对象,使用目标对象 target 的 run()方法 在指定线程组中构造一个线程对象,以name作为它的名字,使 用目标对象target的run()方法 在指定线程组中构造一个线程对象,以name作为它的名字,使 用目标对象target的run()方法,stackSize指定堆栈大小。 说 明
·267·
Java 开发技术大全
死亡:不具备继续运行能力的线程处于死亡状态。这一般是由两种情况引起的: 一种是 run()方法已经运行完毕了,另一种是由其他的线程(一般是主线程)强制 终止它。 需要指出的是: 处于就绪状态的线程是在就绪队列中等待 CPU 资源的, 而一般情况下, 就绪队列中会有多个线程。为此,系统会给每一个线程分配一个优先级,优先级高的可以 排在较前面的位置,能优先得到 CPU 资源。对于优先级相同的线程,一般按照先来先服务 的原则调度。
8.1
线程的概念
在操作系统中,通常将进程看作是系统资源的分配单位和独立运行的基本单位。一个 任务就是一个进程。比如,现在正在运行 IE 浏览器,同时还可以打开记事本,系统就会产 生两个进程。通俗地说,一个进程既包括了它要执行的指令,也包括了执行指令时所需要 的各种系统资源,如 CPU、内存、输入输出端口等。不同进程所占用的系统资源相对独立。 进程具有动态性、并发性、独立性和异步性等特点。 一般情况下, 程序员并不需要对进程有更多的了解。 本书前面编写的程序一旦被执行, 都是独立的进程。它所需要的资源,大多数由操作系统来自动分配,无须程序员操心。 线程是一个比较新的概念, 在 20 世纪 80 年代末才正式被引入。 它在提高系统吞吐率、 有效利用系统资源、改善用户之间的通讯效率以及发挥多处理机的硬件性能等方面都有显 著的作用。因此,线程在现代操作系统中得到了广泛的应用,如 Windows、Unix、Linux 等都提供了多线程机制。 线程是比进程更小的执行单位。某一个进程在执行过程中,可以产生多个线程。每个 线程都有自己相对独立的资源(这个和进程非常相似)、生存周期。线程之间可以共享代 码和数据、实时通信、进行必要的同步操作。 在一个进程中,可以有一个或多个线程的存在。如果程序员不创建线程对象,那么系 统至少会创建一个主线程。
8.1.1 多线程的特点
在基于线程(thread-based)的多任务处理环境中,线程是最小的执行单位。这意味着 一个程序可以同时执行两个或者多个任务的功能。例如,一个文本编辑器可以在打印的同 时格式化文本。所以,多进程程序处理“大图片”,而多线程程序处理细节问题。多线程 程序比多进程程序需要更少的管理费用。进程是重量级的任务,需要分配它们自己独立的 地址空间,进程间通信是昂贵和受限的,进程间的转换也是很需要花费的。但是,线程是
static void sleep(long millis) static void sleep(long millis, int nanos) void start() static void yield()
8.3
多线程程序的编写
8.2 节介绍了 Thread 类,但是如果要编写多线程程序,是无法直接使用该类的。用户 需要继承 Thread 类或实现 Runable 接口才行。无论采用哪一种方法,程序员要做的关键性 操作有三个:
第8章
Java 的多线程机制
轻量级的选手, 它们共享相同的地址空间并且共同分享同一个进程。 线程间通信是便宜的, 线程间的转换也是低成本的。 当 Java 程序使用多进程任务处理环境时, 多进程程序不受 Java 的控制,而多线程则受 Java 控制。 设计好的多线程,能够帮助程序员写出 CPU 最大利用率的高效程序,因为 CPU 的空 闲时间保持最低。这对 Java 运行的交互式的网络互连环境是至关重要的,因为空闲时间是 公共的。举个例子来说,网络的数据传输速率远低于计算机的处理能力,本地文件系统资 源的读写速度远低于 CPU 的处理能力,当然,用户输入也比计算机慢很多。在传统的单线 程环境中, 你的程序必须等待每一个这样的任务完成后, 才能执行下一步——尽管 CPU 有 很多空闲时间。多线程使你能够获得并充分利用这些空闲时间。 进程和线程最大的区别在于: 进程是由操作系统来控制的, 而线程是由进程来控制的。 所以很多由操作系统完成的工作必须交由程序员完成。 前面所写的程序都是单线程的程序, 如果需要设计多线程的程序,难度就要大一些。进程都是相互独立,各自享有各自的内存 空间。而一个进程中的多个线程是共享内存空间的,这意味着它们可以访问相同的变量和 对象,这一方面方便了线程之间的通讯,另一方面又带来了新的问题:多个线程同时访问 一个变量可能会出现意想不到的错误。 在传统的 C/C++、OP 等语言中,都是利用操作系统的多线程支持库来完成多线程的 程序设计,线程之间的同步、异步、并发、互斥等控制起来比较麻烦(当然好的开发环境 也会用类来对这些进行封装)。而 Java 在语言这一级提供了对多线程的支持,它本身就提 供了同步机制,大大方便了用户,降低了设计程序的难度。 编制多线程程序,对于程序员而言是一个极大的挑战。尽管 Java 的线程类已经做得不 错了,但还远称不上完美。如果需要编制大型的、要求可靠性很高的多线程程序,还需要 程序员花费大量的时间来设计和调试。如果要深入介绍线程控制的每一个细节,足够写出 厚厚的一本书。限于篇幅,本章只做一些简明介绍。
8.1.2 线程的状态
Java 中用 Thread 类和它的子类对象来表示线程。 一个线程总是处于下面 5 种状态之一: 新建: 当创建一个 Thread 类和它的子类对象后, 新产生的线程对象处于新建状态, 并获得除 CPU 外所需的资源。 就绪:当处于新建状态的线程被启动后,将进入线程队列等待 CPU 资源。这时, 它已经具备了运行的条件,一旦获得 CPU 资源,就可以脱离创建它的主线程独立 运行了。另外,原来处于阻塞状态的线程结束阻塞状态后,也将进入就绪状态。 运行:当一个就绪状态的线程获得 CPU 时,就进入了运行状态。每个 Thread 类及 其子类对象都有一个 run()方法, 一旦线程开始运行, 就会自动运行该方法。 在 run() 方法中定义了线程所有的操作。 阻塞:一个正在运行的线程因为某种特殊的情况,比如,某种资源无法满足,会 让出 CPU 并暂时停止自身的运行, 进入阻塞状态。 只有当引起阻塞的原因消除时, 它才能重新进入就绪状态。
表中参数 Runnable 是一个接口,将在 8.3.2 小节介绍。
8.2.2
Thread 类的常用方法
为了能让线程正常运行以及方便程序员对线程的控制, Thread 类提供了很多辅助方法。 其中,比较常用的方法如表 8.2 所示。
·268·
第8章
Java 的多线程机制
表 8.2 Thread的常用方法 方 法 名 说 返回线程组中正在运行的线程数目 确定当前运行的线程是否有权限修改线程 判断当前哪个线程正在执行 销毁线程,但不收回资源 显示当前线程中的堆栈信息 将当前线程组中的线程复制到数组tarray中 返回线程的名字 获取线程的优先级 获取线程所属的线程组 当前线程被观测者锁定时,返回真 中断线程 测试当前线程是否被中断 测试线程是否已经正常活动 测试线程是否在后台 测试本线程是否被中断 等待,直到线程死亡 等待线程死亡,但最多只等待millis毫秒 如果类是使用单独的 Runnable对象构造的,将调用 Runnable对象的 run 方法,否则本方法不做任何事情就返回了。如果是子类继承Thread类, 请务必实现本方法以覆盖父类的run方法 将线程标记为后台或者用户线程 设置线程的名字为name 改变线程的优先级, Java 定义了三种级别: Thread.MIN_PRIORITY 、 Thread.MAX_PRIORITY 、 和Thread.NORM_PRIORITY 正在运行的线程睡眠(暂停),参数millis指定毫秒数 正在运行的线程睡眠(暂停),millis指定毫秒数,nanos指定纳秒数 启动线程,JVM会自动调用run()方法 正在运行的线程暂停,同时允许其他的线程运行 明 static int activeCount() void checkAccess() static Thread currentThread() void destroy() static void dumpStack() static int enumerate(Thread[] tarray) String getName() int getPriority() ThreadGroup getThreadGroup() static boolean holdsLock(Object obj) void interrupt() static boolean interrupted() boolean isAlive() boolean isDaemon() boolean isInterrupted() void join() void join(long millis) void run() void setDaemon(boolean on) void setName(String name) void newPriority) setPriority(int