Java多线程-第20章
Java多线程-第20章
1.创建线程
Java是一种支持多线程编程的编程语言。多线程是指在同一程序中同时执行多个独立任务的能力。在Java中,线程是一种轻量级的子进程,它是程序中的最小执行单元。Java的多线程编程可以通过两种方式实现:继承Thread类或实现Runnable接口。
-
继承Thread类:
class MyThread extends Thread {public void run() {// 线程执行的代码} }创建并启动线程:
MyThread myThread = new MyThread(); myThread.start(); // 启动线程 -
实现Runnable接口:
class MyRunnable implements Runnable {public void run() {// 线程执行的代码} }创建并启动线程:
MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); thread.start(); // 启动线程
所有的程序都是通过main方法开始执行的。当一个Java程序启动时,JVM(Java虚拟机)会自动创建一个主线程,该线程负责执行main方法。在多线程编程中,你可以创建额外的线程来执行其他任务。
Java提供了一些关键字和方法来控制线程的执行,其中一些关键字包括:
synchronized:用于控制多个线程访问共享资源时的同步问题。wait()、notify()、notifyAll():用于实现线程间的通信和协调。sleep(long milliseconds):让线程休眠一段时间。join():等待一个线程终止。yield():让出CPU执行权,让其他线程执行。
多线程编程的主要挑战之一是避免竞态条件(Race Condition)和死锁(Deadlock)。竞态条件发生在多个线程试图同时访问和修改共享数据时,而死锁则是线程相互等待对方释放资源,导致所有线程都无法继续执行的情况。
线程的状态有以下几种:
- 新建(New): 线程已经创建,但还没有开始执行。
- 就绪(Runnable): 线程可以开始执行,等待CPU时间片。
- 运行(Running): 线程正在执行。
- 阻塞(Blocked): 线程被阻塞,等待某个事件的发生。
- 死亡(Terminated): 线程执行完成。
请注意,Java的多线程编程也有一些高级的概念和工具,如线程池、Callable和Future等,用于更灵活地处理多线程任务。
实例1:让线程循环打印1-10的数字

实例2:让窗口中的图标动起来

2.线程的生命周期
Java线程的生命周期描述了一个线程从创建到运行再到结束的整个过程,它包括多个状态,每个状态代表了线程在不同阶段的状态。Java线程的生命周期可以分为以下几个状态:
- 新建状态(New):
- 当线程对象被创建时,它处于新建状态。
- 此时,线程还没有开始执行。
- 就绪状态(Runnable):
- 当线程调用
start()方法后,线程进入就绪状态。 - 此时,线程已经准备好运行,等待获取CPU时间片。
- 当线程调用
- 运行状态(Running):
- 当就绪状态的线程获取到CPU时间片时,线程进入运行状态。
- 此时,线程正在执行其任务。
- 阻塞状态(Blocked):
- 线程在运行过程中,可能由于某些原因需要暂时放弃CPU时间片,进入阻塞状态。
- 典型的例子包括等待I/O完成、等待获取锁、等待通知等。
- 当阻塞条件解除时,线程会重新进入就绪状态。
- 等待状态(Waiting):
- 线程在等待某个条件满足时,会进入等待状态。
- 调用
Object.wait()、Thread.join()、LockSupport.park()等方法可以使线程进入等待状态。 - 等待状态的线程需要其他线程通知或中断才能继续执行。
- 超时等待状态(Timed Waiting):
- 线程在等待一段时间后会进入超时等待状态。
- 调用带有超时参数的
Object.wait()、Thread.sleep()、Thread.join()等方法会导致线程进入超时等待状态。
- 终止状态(Terminated):
- 线程执行完任务或者发生了未捕获的异常时,线程进入终止状态。
- 一个终止状态的线程不能再次启动。
这些状态构成了线程的生命周期,如下图所示:
New -> Runnable -> (Running) -> Blocked -> (Runnable) -> (Terminated)\-> Waiting -> (Runnable) -> (Terminated)\-> Timed Waiting -> (Runnable) -> (Terminated)
注意,生命周期中的括号表示这些状态可能是短暂的,线程可能在运行、等待、超时等待等状态间切换。在实际的多线程应用中,正确地管理线程生命周期是至关重要的,以避免潜在的问题,如死锁、竞态条件等。
3.操作线程的方法
4.1线程的休眠
线程休眠是通过Thread.sleep(long milliseconds)方法实现的。这个方法让当前正在执行的线程在指定的时间内进入休眠状态(即暂停执行),单位是毫秒。在指定时间过去或者线程被中断时,线程将恢复执行。
方法签名为:
public static native void sleep(long millis) throws InterruptedException;
millis:休眠时间,以毫秒为单位。
注意,sleep方法可能抛出InterruptedException异常,因为线程在休眠时可以被其他线程中断。在处理中断时,可以选择捕获该异常并处理,或者将异常继续传播出去。
4.2线程的加入
在Java中,可以使用join()方法来等待一个线程完成其执行。join()方法的作用是使当前线程等待调用join()方法的线程执行结束,然后再继续执行当前线程。
方法签名为:
public final void join() throws InterruptedException;
或者可以使用带有超时参数的join(long millis)方法:
javaCopy code
public final synchronized void join(long millis) throws InterruptedException;
millis:等待的最大时间(以毫秒为单位)。
以下是一个简单的例子,演示了线程的加入:
class MyThread extends Thread {public void run() {for (int i = 1; i <= 5; i++) {System.out.println("Task " + i + " in progress by " + Thread.currentThread().getName());try {// 模拟任务执行时间Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}public class JoinExample {public static void main(String[] args) {MyThread thread1 = new MyThread();MyThread thread2 = new MyThread();// 启动线程1thread1.start();try {// 等待线程1执行完成,然后再继续执行主线程thread1.join();} catch (InterruptedException e) {e.printStackTrace();}// 启动线程2thread2.start();// 主线程继续执行System.out.println("Main thread continues its work.");}
}
在这个例子中,主线程启动了thread1,然后调用thread1.join()等待thread1执行完成,接着启动了thread2。由于join()方法会使主线程等待被调用的线程执行完成,所以在这个例子中主线程会等待thread1执行完成后再启动thread2。
4.3线程的中断
线程的中断是一种线程间的协作机制,它允许一个线程通知另一个线程,以请求它停止正在执行的任务。线程的中断通过调用interrupt()方法来触发。
-
中断线程:
-
使用
interrupt()方法中断线程。Thread myThread = new MyThread(); myThread.start(); // ... myThread.interrupt(); // 中断线程 -
interrupt()方法会设置线程的中断标志位,但并不会立即停止线程的执行。线程需要检查自己的中断状态并在适当的时候终止执行。
-
-
检查中断状态:
-
使用
Thread.interrupted()方法检查当前线程的中断状态,并清除中断状态。if (Thread.interrupted()) {// 线程已被中断,执行相应的处理 } -
或者使用
isInterrupted()方法检查线程的中断状态而不清除中断状态。if (myThread.isInterrupted()) {// 线程已被中断,执行相应的处理 }
-
-
处理中断:
-
在线程的执行过程中,可以通过检查中断状态来决定是否停止执行。
public void run() {while (!Thread.interrupted()) {// 执行任务} } -
或者在抛出
InterruptedException异常的地方处理中断。public void run() {try {while (true) {// 执行任务if (Thread.interrupted()) {throw new InterruptedException();}}} catch (InterruptedException e) {// 处理中断异常} } -
在处理中断时,可以选择终止线程的执行,或者采取其他适当的措施。
-
中断通常用于优雅地停止线程,而不是强制终止线程。这种协作的方式允许线程在中断请求到来时,完成正在进行的工作,并进行清理工作,提高程序的健壮性。
实例3:单击按钮停止进度条滚动

4.4线程的礼让
线程的礼让是指一个线程表明自己愿意让出当前的CPU执行时间,以便让其他线程有机会执行。我们可以使用Thread.yield()方法来实现线程的礼让。
方法签名为:
public static native void yield();
Thread.yield()方法是一个静态方法,调用它的线程会让出一些时间片,以便其他具有相同或更高优先级的线程有机会执行。然而,yield()方法并不能保证线程会让出CPU执行权,它只是向调度器发出一个提示。
5.线程的优先级
线程调度器使用线程的优先级来决定哪个线程应该优先执行。线程的优先级是一个整数值,范围从Thread.MIN_PRIORITY(1)到Thread.MAX_PRIORITY(10)。默认情况下,每个线程的优先级都是Thread.NORM_PRIORITY(5)。
线程的优先级可以通过setPriority(int priority)方法进行设置。该方法必须在启动线程之前调用。
以下是设置线程优先级的例子:
class MyThread extends Thread {public void run() {for (int i = 1; i <= 5; i++) {System.out.println("Task " + i + " in progress by " + Thread.currentThread().getName());}}
}public class PriorityExample {public static void main(String[] args) {MyThread thread1 = new MyThread();MyThread thread2 = new MyThread();// 设置线程1的优先级为最高thread1.setPriority(Thread.MAX_PRIORITY);// 启动线程1thread1.start();// 启动线程2thread2.start();}
}
在这个例子中,thread1的优先级被设置为最高(Thread.MAX_PRIORITY),而thread2使用默认的优先级。在运行时,具有更高优先级的线程更有可能被调度执行,但并不能保证绝对顺序。
注意,线程优先级的调整并不是在所有平台上都能生效的,而且过度依赖线程优先级可能导致可移植性问题。在实际应用中,更重要的是编写稳健的多线程代码,而不是过分关注线程优先级。
实例4:观察不同优先级的线程执行完毕顺序

6.线程同步
线程同步是一种机制,用于防止多个线程同时访问共享资源,从而避免数据不一致性和竞态条件。在Java中,主要的线程同步机制包括使用synchronized关键字、wait()、notify()和notifyAll()方法、以及Lock和Condition接口等。
6.1线程安全
线程安全是指多个线程访问某个共享资源时,不会出现不确定的结果或导致不一致性的情况。在多线程环境中,如果没有适当的同步机制,共享的数据结构可能会被多个线程同时修改,从而导致数据不一致或其他问题。确保线程安全是多线程编程中非常重要的一个方面。
以下是一些确保线程安全的常见方式:
-
使用同步方法: 在方法上使用
synchronized关键字,确保一次只有一个线程可以执行该方法。public synchronized void synchronizedMethod() {// 同步的代码块 } -
使用同步代码块: 在代码块中使用
synchronized关键字,确保一次只有一个线程可以执行同步代码块。public void someMethod() {// 非同步代码synchronized (lockObject) {// 同步的代码块}// 非同步代码 } -
使用
java.util.concurrent包中的线程安全类: Java提供了一些线程安全的数据结构,如ConcurrentHashMap、CopyOnWriteArrayList等。Map<String, String> concurrentMap = new ConcurrentHashMap<>(); List<String> copyOnWriteList = new CopyOnWriteArrayList<>(); -
使用
Lock和Condition: 使用Lock接口及其实现类来提供更细粒度的同步控制。Lock lock = new ReentrantLock(); Condition condition = lock.newCondition();lock.lock(); try {// 临界区的代码 } finally {lock.unlock(); } -
使用
volatile关键字:volatile关键字可以保证变量的可见性,但不能解决复合操作的原子性问题。private volatile boolean flag = false; -
使用原子类:
java.util.concurrent.atomic包中提供了一些原子类,如AtomicInteger、AtomicLong等,用于执行原子操作。AtomicInteger atomicInt = new AtomicInteger(0); atomicInt.incrementAndGet();
确保线程安全是一个综合性的问题,需要在设计阶段考虑,并采用适当的同步措施。选择合适的同步机制取决于具体的应用场景和性能要求。在设计和实现多线程程序时,充分了解并考虑线程安全性是至关重要的。
实例5:开发线程安全的火车售票系统

6.2线程同步机制
线程同步机制是一组用于确保多个线程访问共享资源时不会发生竞态条件和数据不一致的技术。以下是一些常见的线程同步机制:
-
synchronized 关键字:
synchronized关键字用于修饰方法或代码块,确保在同一时刻最多只有一个线程能够进入被synchronized修饰的方法或代码块。
// 同步方法 public synchronized void synchronizedMethod() {// 同步的代码块 }// 同步代码块 public void someMethod() {// 非同步代码synchronized (lockObject) {// 同步的代码块}// 非同步代码 } -
Lock 和 Condition 接口:
Lock接口提供了比synchronized更灵活的锁定机制。通过ReentrantLock实现类,可以使用lock()和unlock()方法来控制临界区的访问。Condition接口用于在Lock上创建条件变量,通过await()、signal()和signalAll()方法实现更灵活的线程通信。
Lock lock = new ReentrantLock(); Condition condition = lock.newCondition();lock.lock(); try {// 临界区的代码 } finally {lock.unlock(); } -
volatile 关键字:
volatile关键字用于声明变量,确保线程之间对该变量的写入和读取操作是可见的。它不提供原子性,仅仅保证了可见性。
private volatile boolean flag = false; -
Atomic 类:
java.util.concurrent.atomic包中提供了一组原子类,如AtomicInteger、AtomicLong,用于执行原子操作,避免竞态条件。
AtomicInteger atomicInt = new AtomicInteger(0); atomicInt.incrementAndGet(); -
ReadWriteLock 接口:
ReadWriteLock接口提供了读写锁,允许多个线程同时读取共享资源,但只允许一个线程写入。
ReadWriteLock rwLock = new ReentrantReadWriteLock(); rwLock.readLock().lock(); // 读取共享资源的操作 rwLock.readLock().unlock();rwLock.writeLock().lock(); // 写入共享资源的操作 rwLock.writeLock().unlock();
这些机制可以根据具体的应用场景选择使用,每种机制都有其适用的情况。合理选择同步机制可以提高多线程程序的性能和可维护性,避免潜在的并发问题。
相关文章:
Java多线程-第20章
Java多线程-第20章 1.创建线程 Java是一种支持多线程编程的编程语言。多线程是指在同一程序中同时执行多个独立任务的能力。在Java中,线程是一种轻量级的子进程,它是程序中的最小执行单元。Java的多线程编程可以通过两种方式实现:继承Threa…...
寿险公司通过开源治理保障数字创新,安全打通高质量服务新通道
某寿险公司致力于为消费者提供人性化的产品和服务,在中国保险市场中始终保持前列。该寿险公司以挖掘和满足客户需求为出发点,从产品开发、渠道销售、运营流程和售后服务等各环节,借助数字化工具,不断地努力探索并提升服务品质。 精…...
SpringBoot中的部分注解
1.SpringBoot/spring SpringBootApplication: 包含Configuration、EnableAutoConfiguration、ComponentScan通常用在主类上; Repository: 用于标注数据访问组件,即DAO组件; Service: 用于标注业务层组件; RestController: 用…...
蓝桥杯-02-蓝桥杯C/C++组考点与14届真题
文章目录 蓝桥杯C/C组考点与14届真题参考资源C/C组考点1. 组别2. 竞赛赛程3. 竞赛形式4. 参赛选手机器环境5. 试题形式5.1. 结果填空题5.2. 编程大题 6. 试题考查范围7. 答案提交8. 评分9. 样题样题 1:矩形切割(结果填空题)样题 2:…...
计算机杂谈系列精讲100篇-【计算机应用】关于TensorFlow和PyTorch的一些看法
目录 前言 知识储备 PyTorch使用高频代码 导入包和版本查询...
Uni-App知识点
文章目录 一、事件总线二、什么是事件总线三、触发事件1、监听事件2、只监听一次3、移除监听4、触发事件注意事项5、代码示例6、注意事项 一、事件总线 除了父子组件传参之外,兄弟组件之间共享信息也是我们经常会遇到的。如果遇到这类问题,我们现在可以…...
Postman如何使用(四):接口测试
一.接口 1.程序内部接口:方法与方法之间,模块与模块之间的交互,程序内部抛出的接口,比如bbs系统,有登录模块,发帖模块等等,那你要发帖就必须先登录,那么这两个模块就得有交互&#…...
【Qt绘图】之绘制坦克
使用绘图事件,绘制坦克。 效果 效果很逼真,想象力,有没有。 示例 代码像诗一样优雅,有没有。 包含头文件 #include <QApplication> #include <QWidget> #include <QPainter>绘制坦克类 class TankWidge…...
【机器视觉技术栈】- 机器视觉基础
1.1 为什么采用机器视觉 人眼与机器视觉对比 人眼机器视觉精确性差,64灰度级,不能分辨小于100微米的目标强,256灰度级,可检测微米级目标速度慢,无法看清间隔小于40毫秒的运动目标快,快门时间可达10微秒适…...
Arkts开发UIAbility组件生命周期启动模式开发详解【鸿蒙专栏-19】
文章目录 HarmonyOS UIAbility组件详解UIAbility组件概述声明配置UIAbility组件生命周期Create状态WindowStageCreate和WindowStageDestroy状态Foreground和Background状态Destroy状态UIAbility组件启动模式Singleton启动模式Standard启动模式Specified启动模式HarmonyOS UIAbi…...
力扣295. 数据流的中位数(java,堆解法)
Problem: 295. 数据流的中位数 文章目录 题目描述思路解题方法复杂度Code 题目描述 思路 由于该题目的数据是动态的我们可以维护两个堆来解决该问题 1.维护一个大顶堆,一个小顶堆 2.每个堆中元素个数接近n/2;如果n是偶数,两个堆中的数据个数…...
open3d-点云及其操作
open3d提供了一个专门用于点云的数据结构 PointCloud。 class PointCloud(Geometry3D):color # 颜色normals # 法向量points # 点云def __init__(self, *args, **kwargs):"""__init__(*args, **kwargs)Overloaded function.1. __init__(self: open3d.cpu.py…...
无人机助力电力设备螺母缺销智能检测识别,python基于YOLOv7开发构建电力设备螺母缺销高分辨率图像小目标检测系统
传统作业场景下电力设备的运维和维护都是人工来完成的,随着现代技术科技手段的不断发展,基于无人机航拍飞行的自动智能化电力设备问题检测成为了一种可行的手段,本文的核心内容就是基于YOLOv7来开发构建电力设备螺母缺销检测识别系统…...
如何使用Python的Open3D开源库进行三维数据处理
简介 在本文中,我提供了一个关于如何使用Python的Open3D库(一个用于3D数据处理的开源库)来探索、处理和可视化3D模型的快速演练。 使用Open3D可视化的3D模型(链接https://sketchfab.com/3d-models/tesla-model-s-plaid-9de8855fa…...
HarmonyOS应用开发者基础认证试题
判断题 1.Ability是系统调度应用的最小单元,是能够完成一个独立功能的组件。一个应用可以包含一个或多个Ability。(true) 2.Tabs组件仅可包含子组件TabsContent,每一个页签对应一个内容视图即TabContet组件。(true) 3.使用http模块发起网络请求时&#…...
Android Camera2开启电子防抖(EIS)和光学防抖(OIS)
刚好当前项目有录像功能,使用了第三方框架是基于Camera2引擎开发,当使用 Camera2 API 开发相机应用时,启用和关闭 EIS(电子防抖)是一个重要的功能。EIS 可以帮助减少相机拍摄时的抖动,从而提高图像和视频的…...
劲爆:Sam Altman 回归CEO专访确认Q*的存在
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
Electronica慕尼黑电子展 Samtec团队与21ic分享虎家产品与方案
【摘要/前言】 “希望但凡是能够使用到连接器的场合都有Samtec的身影” 在慕尼黑上海电子展现场,Samtec华东区销售经理章桢彦先生在与21ic副主编刘岩轩老师的采访中,如是说道。这是一种愿景,更是Samtec的努力方向。短短一句话,…...
Vue基本使用(一)
📑前言 本文主要是【Vue】——Vue基本使用的文章,如果有什么需要改进的地方还请大佬指出⛺️ 🎬作者简介:大家好,我是听风与他🥇 ☁️博客首页:CSDN主页听风与他 🌄每日一句&#x…...
Android:BackStackRecord
BackStackRecord:fragment回退栈,继承自FragmentTransaction,并且实现了OpGenerator接口,OpGenerator接口用来添加或弹出事务的,后面会提到。 从《Android:从源码看FragmentManager如何工作》文章知道,每次beginTransaction会创建一个BackStackRecord对象,改对象持有f…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
