线程二:多线程基础
1.2、为什么要使用多线程
- 耗时的操作使用线程,提高应用程序响应
- 并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求。
- 多CPU系统中,使用线程提高CPU利用率
- 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程(拆分任务),成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。
1.3、多线程应用场景
任务量比较大,通过多线程可以提高效率时,需要异步处理时,占用系统资源,造成阻塞的工作时,都可以采用多线程提高效率
- 后台任务:如定时向大量(100W以上)的用户发送邮件;定期更新配置文件、任务调度(如quartz),一些监控用于定期信息采集【MQ方式】
- 自动作业处理:比如定期备份日志、定期备份数据库
- 异步处理:如发文章【图片审核、文章审核】、记录日志【热点文章收集】,更新文章的索引,大批量数据的核对工作(有10万个手机号码,核对哪些是已有用户)
- 数据库的数据分析(待分析的数据太多),数据迁移
- 多步骤的任务处理,可根据步骤特征选用不同个数和特征的线程来协作处理,多任务的分割,由一个主线程分割给多个线程完成【并行结算】
1.4、线程基本操作
【1】线程创建
【2】用户线程和守护线程
Java分为两种线程:用户线程和守护线程
守护线程:
在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因 此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。
将线程转换为守护线程可以通过调用 Thread 对象的 setDaemon(true) 方法来实现。使用守护线程时需要注意:
-
thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。
-
守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。
【3】线程优先级
线程的切换是由线程调度控制的,我们无法通过代码来干涉,但是我们通过提高线程的优先级来最大程度的改善线程获取时间片的几率,当然我们也只是尽力干扰,而不是绝对说优先级高的一定先执行
线程的优先级被划分为10级,值分别为1-10【1最低,10最高】线程提供了3个常量来表示最低,最高,以及默认优先级:
Thread.MIN_PRIORITY 1
Thread.MAX_PRIORITY 10
Thread.NORM_PRIORITY 5
thread.setPriority(int priority):设置线程的优先级。
【4】线程运行状态
新建(NEW)
可运行(RUNNABLE)
运行(RUNNING)
阻塞(BLOCKED)
死亡(DEAD)
【5】线程常用API
常用线程api方法 | |
---|---|
start() | 启动线程 |
getID() | 获取当前线程ID Thread-编号 该编号从0开始 |
getName() | 获取当前线程名称 |
Stop() | 停止线程,(已废弃) |
getPriority(); | 返回线程的优先级【1-10】 |
boolean isAlive() | 测试线程是否处于活动状态 |
isDaemon(): | 测试线程是否为守护线程【伴随用户线程执行】 |
isInterrupted(); | 测试线程是否已经中断 |
Thread.currentThread() | 获取当前线程对象 |
Thread.state getState() | 获取线程的状态 |
常用线程构造函数 | |
Thread() | 分配一个新的 Thread 对象 |
Thread(String name) | 分配一个新的 Thread对象,具有指定的 name正如其名。 |
Thread(Runable r) | 分配一个新的 Thread对象 |
Thread(Runable r, String name) | 分配一个新的 Thread对象 |
其他方法 | |
static void sleep(long ms) | Thread的静态方法sleep用于使当前线程进入阻塞状态,等时间到达之后自动唤醒 |
static void yield() | 该方法用于使当前线程主动让片出当次CPU时间片回到Runnable状态,等待分配时间片. |
t1.join() | 线程调用了join方法,那么就要一直运行到该线程运行结束,才会运行其他进程. 这样可以控制线程执行顺序。 |
stop 和 interrupt
在开发中,经常会遇到需要停止一个正在运行的线程的场景,以前的做法是通过Thread.stop() 的方式来停止具体的线程,但是这个方法目前是被废弃掉的,不推荐使用。不推荐使用的原因如下
-
该方式是通过立即抛出ThreadDeath异常来达到停止线程的目的,而且此异常抛出可能发生在程序的任何一个地方。包括catch、finally等语句块中
-
由于抛出ThreadDeath异常,会导致该线程释放所持有的所有的锁,而且这种释放的时间点是不可控制的,可能会导致出现线程安全问题和数据不一致情况,比如在同步代码块中在执行数据更新操作时线程被突然停止。
因此,为了避免Thread.stop()带来的问题,推荐使用被称作为Interrupt(中断)的协作机制来停止一个正在运行的线程。在JVM中,每个线程都有一个与之关联的Boolean属性,被称之为中断状态,可以通过Thread.currentThread().isInterrupted()
来获取当前线程的中断状态,初始值为false。中断状态仅仅是线程的一个属性,用以表明该线程是否被中断。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)