【Java 基础篇】Java 多线程详解

在这里插入图片描述

多线程是 Java 编程中的一个重要概念,它允许程序同时执行多个任务,提高了程序的性能和响应能力。本篇博客将深入探讨 Java 多线程,从基础概念到实际应用,适用于 Java 初学者和希望深入了解多线程的开发人员。

什么是多线程?

在开始之前,让我们来了解一下什么是线程。线程是程序执行的最小单位,它是进程的一部分,可以独立执行代码。多线程是指在同一个程序中同时运行多个线程。每个线程都有自己的执行路径,可以独立执行任务。

多线程的好处在于可以充分利用多核处理器的性能,提高程序的并发性,实现更高的吞吐量和响应速度。Java 提供了丰富的多线程支持,使得多线程编程变得更加容易。

创建线程

在 Java 中,有两种主要的创建线程的方式:

1. 继承 Thread

可以创建一个继承自 Thread 类的子类,并重写 run 方法来定义线程的任务。然后通过创建子类的实例来启动线程。

class MyThread extends Thread {public void run() {// 线程执行的任务}
}// 创建并启动线程
MyThread thread = new MyThread();
thread.start();

2. 实现 Runnable 接口

可以实现 Runnable 接口,然后通过创建 Thread 类的实例并传递 Runnable 对象来启动线程。

class MyRunnable implements Runnable {public void run() {// 线程执行的任务}
}// 创建 Runnable 对象
MyRunnable myRunnable = new MyRunnable();// 创建并启动线程
Thread thread = new Thread(myRunnable);
thread.start();

线程的生命周期

线程在其生命周期内经历多个状态,包括新建、就绪、运行、阻塞和终止等状态。了解线程的生命周期有助于更好地管理和调试多线程程序。

  1. 新建状态(New):线程已创建,但尚未启动执行。
  2. 就绪状态(Ready):线程已准备好运行,等待系统分配 CPU 时间片。
  3. 运行状态(Running):线程正在 CPU 上执行。
  4. 阻塞状态(Blocked):线程因等待某些事件(如 I/O 操作或锁资源)而被暂停。
  5. 终止状态(Terminated):线程执行完毕或因某种原因被终止。

线程同步与互斥

多线程编程面临一个重要问题,即多个线程同时访问共享资源可能导致数据不一致性和竞态条件。为了解决这些问题,Java 提供了同步机制和锁来确保线程安全。

同步块

使用 synchronized 关键字创建同步块,确保只有一个线程可以访问同步块内的代码。

public synchronized void synchronizedMethod() {// 同步块内的代码
}

Java 中的锁用于协调多个线程对共享资源的访问。常见的锁包括 ReentrantLocksynchronized

ReentrantLock lock = new ReentrantLock();lock.lock(); // 获取锁
try {// 访问共享资源
} finally {lock.unlock(); // 释放锁
}

线程通信

线程之间的通信是多线程编程的关键部分。Java 提供了多种机制来实现线程通信,包括 wait()notify()notifyAll() 等方法。

class SharedResource {private boolean flag = false;public synchronized void produce() throws InterruptedException {while (flag) {wait(); // 等待消费者消费}// 生产数据flag = true;notify(); // 唤醒消费者}public synchronized void consume() throws InterruptedException {while (!flag) {wait(); // 等待生产者生产}// 消费数据flag = false;notify(); // 唤醒生产者}
}

线程池

线程池是一种管理和复用线程的机制,可以有效降低线程创建和销毁的开销。Java 提供了 Executor 框架来创建和管理线程池。

ExecutorService executor = Executors.newFixedThreadPool(5); // 创建固定大小的线程池
executor.submit(new MyRunnable()); // 提交任务给线程池
executor.shutdown(); // 关闭线程池

异常处理

多线程编程可能导致各种异常,如 InterruptedExceptionIllegalThreadStateException 等。合适的异常处理对于程序的稳定性至关重要。

try {// 可能抛出异常的代码
} catch (Exception e) {// 处理异常
} finally {// 最终执行的代码
}

线程安全性与性能

在多线程编程中,线程安全性和性能之间存在权衡关系。过多的同步和锁可能导致性能下降,因此需要根据具体情况进行权衡和优化。

多线程的更多操作方法

除了上述介绍的基本概念和常见操作,Java 多线程还涉及到一些更高级的使用方法和技巧,以下将进一步探讨这些方面。

1. 守护线程(Daemon Threads)

守护线程是一种在后台运行的线程,它的生命周期取决于其他非守护线程是否运行结束。当所有非守护线程结束时,JVM 会自动终止守护线程,而不管它们是否执行完毕。要将线程设置为守护线程,可以使用 setDaemon(true) 方法。

Thread daemonThread = new Thread(new Runnable() {public void run() {while (true) {// 后台任务}}
});
daemonThread.setDaemon(true);
daemonThread.start();

2. 线程优先级

Java 中的线程可以设置优先级,优先级较高的线程在竞争 CPU 时间片时更有可能获得执行机会。线程的优先级范围从1到10,默认为5。可以使用 setPriority() 方法设置线程的优先级。

Thread highPriorityThread = new Thread(new Runnable() {public void run() {// 优先级较高的任务}
});
highPriorityThread.setPriority(Thread.MAX_PRIORITY);Thread lowPriorityThread = new Thread(new Runnable() {public void run() {// 优先级较低的任务}
});
lowPriorityThread.setPriority(Thread.MIN_PRIORITY);

注意:线程优先级的精确行为在不同的操作系统上可能有所不同,不应过度依赖线程优先级。

3. 线程组(Thread Groups)

线程组是一种组织和管理线程的机制,它允许将多个线程分组并对它们进行统一管理。可以使用 ThreadGroup 类来创建和管理线程组。

ThreadGroup group = new ThreadGroup("MyThreadGroup");Thread thread1 = new Thread(group, new Runnable() {public void run() {// 线程1的任务}
});

4. 线程局部变量(Thread-Local Variables)

线程局部变量是一种在每个线程中都有自己独立副本的变量,互不影响。可以使用 ThreadLocal 类来创建线程局部变量。

ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);// 在线程中设置和获取局部变量
threadLocal.set(42);
int value = threadLocal.get();

线程局部变量常用于在多线程环境下存储线程特有的信息,如用户会话、数据库连接等。

5. Callable 和 Future

除了实现 Runnable 接口外,还可以使用 Callable 接口来表示一个可调用的任务,并且可以返回结果。通过 Future 对象,可以获取异步任务的执行结果。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;public class MyCallable implements Callable<String> {public String call() {// 执行任务return "Task completed";}
}public static void main(String[] args) throws InterruptedException, ExecutionException {Callable<String> callable = new MyCallable();FutureTask<String> futureTask = new FutureTask<>(callable);Thread thread = new Thread(futureTask);thread.start();String result = futureTask.get(); // 获取任务结果System.out.println(result);
}

CallableFuture 是 Java 并发编程中非常有用的工具,可用于处理需要返回结果的多线程任务。

6. 并发集合(Concurrent Collections)

Java 提供了一些线程安全的集合类,如 ConcurrentHashMapConcurrentLinkedQueue 等,这些集合类可以在多线程环境下安全地进行读写操作。

ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("key1", 1);
map.put("key2", 2);ConcurrentLinkedQueue<Integer> queue = new ConcurrentLinkedQueue<>();
queue.offer(42);

这些集合类可以减少在多线程环境下使用显式锁的需求,提高了程序的并发性能。

7. 并行编程框架

Java 提供了并行编程框架,如 Fork/Join 框架,可以简化并行任务的拆分和执行。这对于处理大规模并行任务非常有用。

import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;public class MyTask extends RecursiveTask<Integer> {protected Integer compute() {// 执行任务}
}public static void main(String[] args) {ForkJoinPool pool = new ForkJoinPool();MyTask task = new MyTask();int result = pool.invoke(task);
}

注意事项

当编写多线程应用程序时,需要注意以下一些重要的事项,以确保程序的正确性和性能:

  1. 线程安全性(Thread Safety): 多个线程可能同时访问共享数据,确保共享数据的安全性至关重要。使用同步机制,如 synchronized 块或 Lock 接口,来保护共享资源的访问。

  2. 死锁(Deadlock): 死锁是指两个或多个线程相互等待对方释放资源的情况,从而导致程序无法继续执行。要小心避免死锁,使用适当的同步策略和资源分配算法。

  3. 竞态条件(Race Condition): 竞态条件发生在多个线程试图同时修改共享数据时,导致不确定的结果。使用同步机制和原子操作来避免竞态条件。

  4. 性能考虑: 多线程应用程序的性能可能受到线程创建和销毁的开销,以及线程间的竞争条件的影响。使用线程池等技术来管理线程的生命周期,以提高性能。

  5. 线程间通信: 多个线程可能需要相互通信和协调工作。使用 wait()notify()notifyAll() 等方法来实现线程间的有效通信。

  6. 资源管理: 确保在使用完资源后正确地释放它们,以避免资源泄漏。对于文件、数据库连接等资源,使用 try-with-resources 或手动释放资源。

  7. 异常处理: 要小心处理线程中的异常。线程异常未捕获可能导致整个应用程序崩溃。确保在线程中使用 try-catch 块来捕获并处理异常。

  8. 线程终止: 确保线程在不再需要时正确地终止。避免使用 stop() 方法,而是使用标志位或其他方式来通知线程退出。

  9. 性能调优: 随着线程数量的增加,线程切换和上下文切换的开销也会增加。要根据应用程序的需求和硬件配置来调整线程数量。

  10. 可测试性: 编写可测试的多线程代码可能会更具挑战性。使用模拟对象、单元测试和集成测试来验证多线程代码的正确性。

  11. 避免共享可变状态: 尽可能避免多个线程共享可变状态,而是使用不可变对象或线程本地存储来减少竞态条件。

  12. 线程池: 对于频繁创建和销毁线程的情况,考虑使用线程池来重用线程,以减少开销和提高性能。

  13. CPU 和 I/O 绑定: 确保线程的工作类型与其执行的任务相匹配。CPU 密集型任务可能需要更多的线程,而 I/O 密集型任务可能需要较少的线程。

  14. 线程命名: 为了更好地识别和调试线程,可以为线程设置有意义的名称。

  15. 监控和诊断: 使用工具和库来监控线程的状态、性能和健康情况。常用的工具包括 VisualVM、JConsole、线程转储分析等。

  16. 不要滥用多线程: 不是所有的问题都需要多线程来解决。在考虑使用多线程之前,先评估问题的性质和需求,确保多线程是合理的选择。

遵循这些注意事项可以帮助您编写更可靠、高性能的多线程应用程序,并降低出现问题的可能性。同时,多线程编程需要谨慎和经验,建议在实际应用中不断学习和优化。

总结

本篇博客详细介绍了 Java 多线程编程的基本概念、创建线程、线程生命周期、线程同步与互斥、线程通信、线程池、异常处理等方面的内容。多线程编程是 Java 开发中的重要主题,希望本文能帮助读者更好地理解和应用多线程技术。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/83917.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue之路由及Node.js环境搭建(一起探索新事物)

目录 ​编辑 前言 一、Vue之路由 1.路由简介 1.1 什么是路由 1.2 什么是SPA 1.3 SPA的实现思路 1.4 使用路由的优势 2. 案例演示 2.1 导入所需的js文件 2.2 编写案例代码&#xff08;模拟页面跳转&#xff09; 二、Vue之node.js 1. node.js简介 1.1 什么是node.j…

ubuntu 18.04 中 eBPF samples/bpf 编译

1. history 信息 一次成功编译 bpf 后执行 history 得到的信息&#xff1a; yingzhiyingzhi-Host:~/ex/ex_kernel/linux-5.4$ history1 ls2 mkdir ex3 cd ex4 mkdir ex_kernel5 ls /boot/6 sudo apt install linux-source7 ls /usr/src/8 uname -r9 cd ex_kernel/10…

postman记录backup

之前一直未登录postman&#xff0c;在临时空间处理请求&#xff0c;可能是因为postman更新了&#xff0c;导致其记录没了 别着急&#xff01; 首先我们先登录postman&#xff0c;有谷歌登录方式等 第一步、登录后点击import 第二步、点击files 第三步、找到c:/users/AppData…

20-SpringCloudAlibaba-2

六 分布式流量防护 1 认识分布式流量防护 在分布式系统中&#xff0c;服务之间的相互调用会生成分布式流量。如何通过组件进行流量防护&#xff0c;并有效控制流量&#xff0c;是分布式系统的技术挑战之一。 什么是服务雪崩 假设我有一个微服务系统&#xff0c;这个系统内包…

【WinForm】WinForm窗体程序如何一直运行在屏幕的最前方

文章目录 前言一、新建一个项目二、完整代码三、收起程序总结 前言 程序一致运行在其他软件的最前方&#xff0c;比如让WinForm窗体程序一致运行在微信的最前面。 WinForm窗体程序如何一直运行在屏幕的最前方 一、新建一个项目 新建一个WinForm程序 设置最大化为true&…

R语言RSTAN MCMC:NUTS采样算法用LASSO 构建贝叶斯线性回归模型分析职业声望数据...

全文链接&#xff1a;http://tecdat.cn/?p24456 如果你正在进行统计分析&#xff1a;想要加一些先验信息&#xff0c;最终你想要的是预测。所以你决定使用贝叶斯&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 相关视频 但是&#xff0c;你没有共轭先验。你…

数据结构与算法:排序算法(1)

目录 冒泡排序 思想 代码实现 优化 鸡尾酒排序 优缺点 适用场景 快速排序 介绍 流程 基准元素选择 元素交换 1.双边循环法 使用流程 代码实现 2.单边循环法 使用流程 代码实现 3.非递归实现 排序在生活中无处不在&#xff0c;看似简单&#xff0c;背后却隐藏…

【漏洞复现】JumpServer未授权访问漏洞(CVE-2023-42442)

文章目录 前言声明一、JumpServer简介二、漏洞描述三、影响范围四、资产搜索五、漏洞复现六、修复建议 前言 JumpServer的权限管理存在缺陷&#xff0c;未经授权的远程攻击者可以下载历史会话连接期间的所有操作日志&#xff0c;可导致敏感信息泄漏。 声明 请勿利用文章内的…

Mybatis懒加载

懒加载是什么&#xff1f; 按需加载所需内容&#xff0c;当调用到关联的数据时才与数据库交互否则不交互&#xff0c;能大大提高数据库性能&#xff0c;并不是所有场景下使用懒加载都能提高效率。 Mybatis懒加载&#xff1a;resultMap里面的association、collection有延迟加载功…

视频文本检索(ICCV 23):Unified Coarse-to-Fine Alignment for Video-Text Retrieval

论文作者&#xff1a;Ziyang Wang,Yi-Lin Sung,Feng Cheng,Gedas Bertasius,Mohit Bansal 作者单位&#xff1a;UNC Chapel Hill 论文链接&#xff1a;http://arxiv.org/abs/2309.10091v1 项目链接&#xff1a;https://github.com/Ziyang412/UCoFiA 内容简介&#xff1a; …

Win10专业版开启远程桌面

Win10专业版开启远程桌面 方法一&#xff1a; 一、按“Win R”键&#xff0c;然后输入“sysdm.cpl”并按下回车键打开系统属性。 二、选择“远程”选项卡&#xff0c;在远程桌面中勾选“允许远程连接到此计算机”就可以开启远程桌面&#xff1b; 方法二&#xff1a; 一、打…

评价指标分类

声明 本文是学习GB-T 42874-2023 城市公共设施服务 城市家具 系统建设实施评价规范. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件确立了城市家具系统建设实施的评价原则、评价流程&#xff0c;给出了评价指标&#xff0c;描述了 方…

visual studio 安装包丢失或损坏

visual studio 安装包丢失或损坏 如下图所示为vs2015报错信息。 解决方案&#xff1a; 找到镜像文件或者压缩包的解压位置&#xff1b; 路径&#xff1a;C:\Users\Administrator\Desktop\packages 复制该路径至上图的请提供搜索包的位置。

如何自动获取短信验证码?

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ 这篇文章通过解决实际项目开发中遇到的如何自动获取短信验证码的问题&#xff0c;进一步讲述在Java中如何使用正则。 Java中如何使用正则 Java中正则相关类位于java.util.r…

新款 锐科达 SV-2102VP SIP广播音频模块 RTP流音频广播

新款 锐科达 SV-2102VP SIP广播音频模块 RTP流音频广播 SV-2102VP和 SV-2103VP网络音频模块是一款通用的独立SIP音频功能模块&#xff0c;可以轻松地嵌入到OEM产品中。该模块对来自网络的SIP协议及RTP音频流进行编解码。 本系列模块可以应用于以下领域&#xff1a; • 各种商…

selenium自动化测试-登录网站用户

昨天学习了selenium自动化测试工具的入门&#xff0c;知道了Selenium是用于自动化控制浏览器做各种操作&#xff0c;打开网页&#xff0c;点击按钮&#xff0c;输入表单等等。 今天学习通过selenium自动化测试工具自动登录某网站用户操作。 第一步&#xff1a;确定目标网址 …

【C++进阶】:哈希

哈希 一.unordered_map二.底层结构1.哈希概念2.解决哈希冲突1.闭散列2.开散列 在C98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时效率可达到 l o g 2 N log_2N log2​N&#xff0c;即最差情况下需要比较红黑树的高度次&#xff0c;当树中的…

【C++面向对象侯捷】4.参数传递与返回值

文章目录 构造函数放在 private&#xff1f;>单例模式 const member functions(常量成员函数) > 不会改变数据内容的&#xff0c;加上const参数传递&#xff1a;pass by value vs. pass by reference(to const)【最好传引用&#xff0c;占用空间小】返回值传递&#xff1…

Spire.OCR for .NET 1.9.0 Crack

Spire.OCR for .NET 是一个专业的 OCR 库&#xff0c;用于从 JPG、PNG、GIF、BMP 和 TIFF 格式的图像中读取文本。开发人员可以轻松地在 C# 和 VB.NET 的 .NET 应用程序中添加 OCR 功能。它支持常用的图像格式&#xff0c;并提供从图像中​​读取多个字符和字体、粗体和斜体样式…

CDN内容分发系统

CDN 分发系统的架构。CDN 系统的缓存&#xff0c;也是一层一层的&#xff0c;能不访问后端真正的源&#xff0c;就不打扰它。 在没有 CDN 的情况下&#xff0c;用户向浏览器输入 www.web.com 这个域名&#xff0c;客户端访问本地 DNS 服务器的时候&#xff0c;如果本地 DNS 服务…