第八章多线程

在Java编程的世界里,多线程是实现高效并发、提升程序性能的关键利器。第八章围绕多线程展开,从基础概念到实际应用,构建起完整的知识体系,下面就深入梳理总结 。

一、线程与进程:并发的基础概念

(一)核心区别与联系

进程是程序运行的独立实体,拥有自己的资源(如内存空间 ),一个进程可包含多个线程。线程则是进程内的执行单元,共享进程资源,比进程更轻量,切换成本低。比如打开的一个软件是进程,软件里同时进行的后台更新、界面交互等就是线程,理解二者关系,是踏入多线程编程的第一步 。

(二)在Java中的体现

Java程序运行时,JVM本身就是一个进程,main方法启动的是主线程,我们可以在这个进程里创建多个线程,让它们并发执行任务,充分利用CPU资源,提升程序处理多任务的能力,像后台数据加载同时进行界面渲染,就是多线程的典型应用场景 。

二、线程的创建和启动:开启并发之旅

(一)继承Thread类

定义一个类继承Thread,重写run()方法,把线程要执行的逻辑写在其中。然后创建该类对象,调用start()方法启动线程(注意不是直接调用run(),start()才会真正开启新线程 )。比如:
class MyThread extends Thread {
@Override
public void run() {
// 线程执行逻辑
System.out.println("线程通过继承Thread类启动");
}
}
// 启动
new MyThread().start();
这种方式简单直接,但Java单继承的特性,会限制类的拓展 。

(二)实现Runnable接口

定义类实现Runnable接口,实现run()方法,再把该类对象作为参数传给Thread类构造器,创建Thread对象后调用start()。示例:
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程通过实现Runnable接口启动");
}
}
new Thread(new MyRunnable()).start();
它避免了单继承局限,还能实现资源共享(多个线程共享Runnable实现类对象 ),更灵活,实际开发中常用 。

(三)JDK8+的Lambda表达式简化

对于简单的Runnable实现,可用Lambda表达式简化,如:
new Thread(() -> {
System.out.println("用Lambda简化线程创建启动");
}).start();
让代码更简洁,适合快速编写简单线程逻辑 。

三、线程的生命周期及状态转换:理解线程“一生”

(一)主要状态

线程有新建、就绪、运行、阻塞、终止等状态。新建是刚创建Thread对象;调用start()进入就绪,等待CPU调度;被调度后进入运行,执行run()逻辑;遇到阻塞条件(如sleep、等待锁 )进入阻塞,阻塞解除又回到就绪;run()执行完或异常终止则进入终止状态 。

(二)状态转换触发

通过调用start()(新建→就绪 )、获得CPU时间片(就绪→运行 )、sleep()/等待同步锁(运行→阻塞 )、阻塞条件消失(阻塞→就绪 )、run()结束(运行→终止 )等操作,推动状态转换,理解这些,能更好把控线程执行流程,排查线程相关问题 。

四、线程同步:解决并发冲突

(一)线程安全问题

多线程共享资源时,若同时读写,会出现数据不一致。比如多个线程操作同一个计数器,可能导致计数错误,这就是线程安全隐患,像售票系统中,多个窗口同时卖票,不处理同步,就可能卖出重复票或超售票 。

(二)线程同步方式

• 同步代码块:用synchronized关键字包裹共享资源操作代码,指定锁对象(如 synchronized (this) { // 操作共享资源 } ),保证同一时间只有一个线程能执行该代码块,锁对象要保证多线程共享 。

• 同步方法:把synchronized加在方法上,非静态方法锁是this,静态方法锁是类对象,让整个方法成为同步区域,限制线程访问,如:
public synchronized void sellTicket() {
// 售票逻辑
}
• Lock接口:JDK5+引入,如ReentrantLock,比synchronized更灵活,可手动获取和释放锁,支持公平锁等特性。示例:
Lock lock = new ReentrantLock();
lock.lock();
try {
// 操作共享资源
} finally {
lock.unlock(); // 确保释放锁,避免死锁
}
五、线程的控制:灵活调度线程

(一)常见控制方法

• sleep(long millis):让线程休眠指定时间,休眠时不释放锁,到时间后回到就绪状态,可用于模拟延时等场景,如Thread.sleep(1000); 让线程暂停1秒 。

• join():等待该线程执行完毕,其他线程再继续,比如主线程启动子线程后,调用subThread.join(); ,主线程会等子线程跑完再执行后续代码,用于协调线程执行顺序 。

• interrupt():中断线程,设置中断标志,需线程内部配合处理中断逻辑(通过isInterrupted()判断 ),不是强制终止线程,避免粗暴中断导致资源泄漏 。

六、线程池:高效管理线程资源

(一)线程池优势

频繁创建销毁线程开销大,线程池可预先创建一定数量线程,任务来临时复用线程,减少创建销毁成本;还能控制最大线程数,避免线程过多耗尽系统资源,提升程序稳定性和效率,像Web服务器处理大量请求,用线程池管理线程很常见 。

(二)创建与使用

Java通过ExecutorService等接口和Executors工具类创建线程池,如:

• FixedThreadPool:固定大小线程池,ExecutorService pool = Executors.newFixedThreadPool(5); ,5个线程处理任务,任务队列满了新任务等待 。

• CachedThreadPool:缓存线程池,线程数根据任务动态调整,空闲线程超时回收,适合大量短期任务 。

• 自定义线程池:通过ThreadPoolExecutor构造器,更精细设置核心线程数、最大线程数、存活时间、队列等参数,满足复杂需求,如:
ThreadPoolExecutor pool = new ThreadPoolExecutor(
2, // 核心线程数
5, // 最大线程数
60, // 空闲线程存活时间
TimeUnit.SECONDS,
new LinkedBlockingQueue<>() // 任务队列
);
使用时,通过submit(Runnable task)或execute(Runnable task)提交任务,用完后调用shutdown()等方法关闭线程池 。

七、案例实践:多线程的实际应用

(一)红绿灯系统

模拟交通信号灯的切换,用多线程分别控制红灯、绿灯、黄灯的显示时长和状态转换。不同线程负责不同灯的计时和状态变更,结合线程的启动、休眠、状态控制等知识,让我们直观看到多线程如何协作完成周期性任务,理解线程同步和控制在模拟现实场景中的应用 。

(二)优惠券活动

处理大量用户领取优惠券的请求,用线程池管理线程,避免频繁创建线程的开销。多个线程并发处理领取请求,同时要解决线程安全问题(如优惠券库存共享操作 ),通过同步机制保证库存数据正确,体现多线程在高并发业务场景的实践价值,学习如何平衡效率和数据安全 。

(三)注水排水系统

模拟水池的注水和排水过程,多个线程分别模拟注水口和排水口的工作,涉及共享资源(水池水量 )的并发操作。需要运用线程同步,确保注水和排水操作不会导致水量数据混乱,让我们在模拟物理系统的过程中,深化对多线程同步、控制的理解,掌握解决复杂并发问题的思路 。

八、本章小结与习题

小结梳理了多线程从基础概念(线程与进程 )、创建启动,到生命周期、同步控制、线程池等核心知识,帮我们串联起整个多线程体系。习题则通过不同场景的题目,检验对线程创建、同步处理、线程池使用等知识点的掌握程度,促使我们查漏补缺,进一步巩固多线程编程技能 。

总之,第八章的多线程内容,从理论到实践,深入讲解了Java多线程的方方面面。掌握这些知识,无论是开发高并发的Web应用、高效处理批量任务,还是模拟复杂的并发场景,都能游刃有余。多线程是Java编程中实现高效并发的关键,学好它,能让我们的程序更“聪明”、更高效地运行,应对各种复杂的业务需求 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值