第六章 多线程与并发
第一节 多线程基础
概述
多线程技术是Java语言中非常重要且实用的编程技术之一。随着计算机硬件多核处理器的普及,掌握多线程编程不仅可以提高程序的执行效率,还能实现更复杂的并发处理需求。本节内容主要介绍多线程的基本概念、线程的创建与运行机制、线程的生命周期以及线程间的简单协作方法。通过本节的学习,考生将能够理解多线程的基本原理,掌握Java中线程的使用方法,具备编写简单多线程程序的能力,为后续章节深入学习线程同步与并发控制打下坚实基础。
核心概念
- 线程(Thread):是程序执行的最小单位,是进程中的一个执行路径。一个进程可以包含多个线程,这些线程共享进程的资源。
- 多线程(Multithreading):指在一个程序中同时运行多个线程,实现并发执行。
- 并发(Concurrency):多个线程在同一时间段交替执行,使得程序看起来同时运行多个任务。
- 线程调度(Thread Scheduling):操作系统或Java虚拟机对多个线程分配CPU时间的过程。
- 线程生命周期(Thread Life Cycle):线程从创建到销毁的过程,包含新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、死亡(Terminated)等状态。
原理分析
Java中的多线程是基于Java虚拟机(JVM)和操作系统内核线程实现的。每一个Java线程对应一个操作系统的原生线程。线程调度由操作系统负责,Java线程通过Thread类或实现Runnable接口来创建。线程的运行是由线程调度器根据线程优先级、时间片轮转等策略分配CPU时间。
线程的生命周期管理是多线程编程的重要基础,线程状态变化受程序控制,例如调用start()启动线程,sleep()或wait()使线程阻塞,线程执行完毕则进入终止状态。合理理解和管理线程生命周期,有助于设计高效、稳定的多线程程序。
详细内容
1. 线程的创建方式
Java提供了两种主要的线程创建方式:
继承Thread类
通过继承java.lang.Thread类,重写其run()方法,创建线程类的实例并调用start()方法启动线程。实现Runnable接口
创建一个实现java.lang.Runnable接口的类,实现run()方法,然后将该对象作为参数传递给Thread类的构造方法,再调用start()启动线程。
示例代码:
// 继承Thread类方式
class MyThread extends Thread {
public void run() {
System.out.println("Thread running: " + Thread.currentThread().getName());
}
}
// 实现Runnable接口方式
class MyRunnable implements Runnable {
public void run() {
System.out.println("Runnable running: " + Thread.currentThread().getName());
}
}
public class TestThread {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
Thread t2 = new Thread(new MyRunnable());
t2.start();
}
}
2. 线程的生命周期详解
线程在Java中经历以下几个状态:
- 新建状态(New):线程对象被创建,但尚未调用start()。
- 就绪状态(Runnable):调用start()后,线程进入就绪状态,等待CPU调度。
- 运行状态(Running):线程获得CPU时间片,开始执行run()方法。
- 阻塞状态(Blocked):线程等待某些条件,如等待I/O完成、调用sleep()、wait()、或者被其他线程锁定。
- 死亡状态(Terminated):线程执行完run()方法或被强制终止,生命周期结束。
线程状态转换图:
新建 -> 就绪 -> 运行 ->(阻塞)-> 就绪 -> 运行 -> 死亡
掌握线程状态转换,有助于理解线程调度与控制方法。
3. 线程的启动与运行
- 调用线程对象的start()方法,线程进入就绪状态。
- JVM线程调度器负责将CPU时间片分配给就绪线程,线程进入运行状态。
- 线程执行run()方法完成后,进入死亡状态。
注意: 直接调用run()方法不会启动新线程,而是普通方法调用,代码在当前线程中顺序执行。
4. 线程优先级
Java线程有优先级,范围从1(最低)到10(最高),默认优先级是5。优先级高的线程获得CPU时间片的概率更大,但不保证绝对优先执行。
设置线程优先级的API:
thread.setPriority(Thread.MAX_PRIORITY); // 设置最高优先级
5. 线程休眠与暂停
- sleep(long millis):让当前线程暂停执行指定时间,进入阻塞状态。
- yield():让出当前线程的CPU执行时间,但不保证立即让其他线程执行。
6. 线程的简单通信
线程间可以通过共享变量、wait()和notify()实现简单通信和协作,但本节主要介绍基础内容,深入同步与通信在后续章节。
实例分析
实例一:简单线程创建与启动
背景:模拟两个线程同时输出信息,验证线程启动效果。
代码分析:
- 创建继承Thread的类MyThread,重写run方法输出线程名。
- 创建实现Runnable的类MyRunnable,重写run方法。
- 在main方法中分别启动两个线程。
结论:程序成功启动两个独立线程,输出线程名,体现多线程并发执行的效果。
实例二:线程状态观察
背景:通过代码观察线程不同生命周期的状态变化。
代码实现:
- 创建线程,调用start()后打印线程状态。
- 线程中调用sleep()暂停,并打印状态。
结论:通过Thread.getState()方法可以看到线程执行状态变化,增强对线程生命周期的理解。
实例三:线程优先级影响测试
背景:启动多个线程,设置不同优先级,观察运行顺序和效果。
分析:
- 创建多个线程,分别设置优先级为1、5、10。
- 启动线程后,观察线程执行顺序。
结论:优先级高的线程更可能先执行,但线程调度仍有不确定性,优先级不能完全控制执行顺序。
常见误区
误区1:直接调用run()方法启动线程
错误:直接调用run()只是普通方法调用,没有开启新线程。
正确:必须调用start()方法。误区2:忽视线程安全问题
错误:多个线程操作共享资源时不加同步,导致数据不一致。
正确:后续章节学习同步机制,基础阶段应意识到线程安全重要性。误区3:错误理解线程优先级
错误:认为优先级高的线程一定先执行。
正确:优先级只是调度参考,不保证执行顺序。误区4:线程未正确停止
错误:通过强制停止线程导致资源泄漏。
正确:应通过标志位等安全方式终止线程。误区5:忽视线程生命周期状态
错误:随意操作线程状态,导致程序异常。
正确:应理解生命周期,合理控制线程流程。
应用场景
- GUI界面响应:在图形界面程序中,主线程负责界面响应,子线程执行耗时任务,避免界面卡顿。
- 后台任务处理:服务器程序中使用多线程处理多个客户端请求,实现并发服务。
- 资源监控:定时启动监控线程,后台采集系统状态或日志。
- 数据并行处理:大数据处理中,将任务分割成多个线程并发执行,提高处理效率。
- 实时系统:多线程实现实时数据采集和处理,保证系统响应速度。
知识拓展
- 线程同步机制:后续章节将介绍synchronized关键字、锁机制,解决线程安全问题。
- 线程池技术:Java提供线程池管理,提高线程复用和性能。
- 并发包(java.util.concurrent):包含高效线程工具类,如CountDownLatch、Semaphore、ConcurrentHashMap等。
- 线程通信:wait/notify机制实现线程间协调。
- 线程安全类:例如StringBuffer、AtomicInteger等线程安全实现。
总结回顾
本节系统介绍了Java多线程的基础知识,主要包括:
- 线程的定义与多线程概念
- 两种线程创建方式:继承Thread类和实现Runnable接口
- 线程的生命周期及状态转换
- 线程启动与运行的基本流程
- 线程优先级、休眠与暂停的基本操作
- 通过典型实例理解多线程执行的效果
- 常见误区提示,帮助避免初学者常犯错误
- 多线程的实际应用场景,理解技术价值和应用范围
掌握本节内容是理解和掌握Java并发编程的基础,为后续深入学习线程同步、线程间通信及并发工具类奠定扎实基础。考生应重点理解线程创建、启动及生命周期的概念,熟练编写简单多线程程序,做到理论与实践相结合。
建议复习方法:
- 动手编写线程创建和启动代码,观察线程状态变化
- 理解线程状态转换图,记忆线程生命周期各阶段
- 结合实例巩固线程优先级和休眠的知识
- 反复练习避免误区,确保理解多线程基本概念
通过系统学习和反复实践,考生能够牢固掌握多线程编程的基础内容,为应对全国计算机等级考试二级Java语言程序设计科目多线程部分打下坚实基础。