Java 编码系列:线程基础与最佳实践
引言
在多任务处理和并发编程中,线程是不可或缺的一部分。Java 提供了丰富的线程管理和并发控制机制,使得开发者可以轻松地实现多线程应用。本文将深入探讨 Java 线程的基础知识,包括 Thread
类、Runnable
接口、Callable
接口以及线程的生命周期,并结合大厂的最佳实践和底层核心原理,帮助读者全面掌握这些关键技术。
1. 线程基础
1.1 什么是线程
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个进程可以包含多个线程,这些线程共享进程的资源,如内存和文件句柄。
1.2 线程的优势
- 提高响应速度:通过多线程,可以同时执行多个任务,提高应用程序的响应速度。
- 资源共享:线程共享进程的资源,减少了资源开销。
- 简化编程模型:多线程编程模型使得复杂的任务可以分解为多个简单的任务并行执行。
2. 创建线程的方式
2.1 继承 Thread
类
通过继承 Thread
类并重写 run
方法,可以创建一个新的线程。
class MyThread extends Thread {@Overridepublic void run() {System.out.println("线程 " + Thread.currentThread().getName() + " 运行中...");}
}public class ThreadExample {public static void main(String[] args) {MyThread myThread = new MyThread();myThread.start(); // 启动线程}
}
2.2 实现 Runnable
接口
通过实现 Runnable
接口并实现 run
方法,可以创建一个新的线程。这种方式更加灵活,因为一个 Runnable
对象可以被多个线程共享。
class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("线程 " + Thread.currentThread().getName() + " 运行中...");}
}public class RunnableExample {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread = new Thread(myRunnable, "MyThread");thread.start(); // 启动线程}
}
2.3 实现 Callable
接口
Callable
接口类似于 Runnable
,但它可以返回一个结果,并且可以抛出异常。Callable
接口通常与 Future
和 ExecutorService
一起使用。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i < 100; i++) {sum += i;}return sum;}
}public class CallableExample {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(2);Future<Integer> future = executorService.submit(new MyCallable());try {int result = future.get(); // 获取结果System.out.println("计算结果: " + result);} catch (InterruptedException | ExecutionException e) {e.printStackTrace();} finally {executorService.shutdown(); // 关闭线程池}}
}
3. 线程的生命周期
3.1 线程状态
Java 线程有以下几种状态:
- New:线程被创建但尚未启动。
- Runnable:线程正在 JVM 中运行,但可能在等待操作系统资源。
- Blocked:线程被阻塞,等待监视器锁。
- Waiting:线程在等待另一个线程执行特定操作。
- Timed Waiting:线程在等待指定的时间。
- Terminated:线程已退出。
3.2 线程状态转换
- New -> Runnable:调用
start
方法。 - Runnable -> Blocked:尝试获取已被其他线程持有的锁。
- Runnable -> Waiting:调用
wait
、join
或LockSupport.park
方法。 - Runnable -> Timed Waiting:调用
sleep
、wait(long)
、join(long)
或LockSupport.parkNanos
方法。 - Blocked -> Runnable:获取到所需的锁。
- Waiting -> Runnable:等待的条件满足。
- Timed Waiting -> Runnable:等待时间到期。
- Runnable -> Terminated:线程的
run
方法执行完毕或因未捕获的异常而终止。
4. 线程同步
4.1 同步方法
通过在方法上使用 synchronized
关键字,可以确保同一时间只有一个线程可以访问该方法。
public class SynchronizedMethodExample {private int count = 0;public synchronized void increment() {count++;}public static void main(String[] args) {SynchronizedMethodExample example = new SynchronizedMethodExample();Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("最终计数: " + example.count);}
}
4.2 同步代码块
通过在代码块上使用 synchronized
关键字,可以确保同一时间只有一个线程可以访问该代码块。
public class SynchronizedBlockExample {private int count = 0;private final Object lock = new Object();public void increment() {synchronized (lock) {count++;}}public static void main(String[] args) {SynchronizedBlockExample example = new SynchronizedBlockExample();Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("最终计数: " + example.count);}
}
5. 线程间通信
5.1 wait
和 notify
方法
wait
和 notify
方法用于线程间的通信。wait
方法使当前线程进入等待状态,notify
方法唤醒一个等待的线程。
public class WaitNotifyExample {private final Object lock = new Object();private boolean flag = false;public void producer() {synchronized (lock) {while (flag) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}// 生产数据System.out.println("生产数据");flag = true;lock.notify();}}public void consumer() {synchronized (lock) {while (!flag) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}// 消费数据System.out.println("消费数据");flag = false;lock.notify();}}public static void main(String[] args) {WaitNotifyExample example = new WaitNotifyExample();Thread producerThread = new Thread(() -> {for (int i = 0; i < 5; i++) {example.producer();}});Thread consumerThread = new Thread(() -> {for (int i = 0; i < 5; i++) {example.consumer();}});producerThread.start();consumerThread.start();}
}
6. 线程池
6.1 什么是线程池
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池可以有效控制运行的线程数量,减少创建和销毁线程的开销。
6.2 创建线程池
Java 提供了 ExecutorService
接口和 Executors
工具类来创建和管理线程池。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolExample {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(2);for (int i = 0; i < 5; i++) {int taskId = i;executorService.execute(() -> {System.out.println("任务 " + taskId + " 在线程 " + Thread.currentThread().getName() + " 上运行");});}executorService.shutdown(); // 关闭线程池}
}
7. 大厂最佳实践
7.1 阿里巴巴《Java开发手册》
- 避免滥用线程:合理使用线程池,避免频繁创建和销毁线程。
- 同步方法优先:在多线程环境中,优先使用同步方法或同步代码块。
- 避免死锁:设计线程同步时,避免出现死锁的情况。
7.2 Google Java Style Guide
- 线程安全:确保多线程环境下的代码是线程安全的。
- 资源管理:使用
try-with-resources
语句管理资源,确保资源在使用后正确释放。 - 异常处理:合理处理线程中的异常,避免未捕获的异常导致线程终止。
7.3 Oracle 官方文档
- 线程池:推荐使用
ExecutorService
来管理线程池,提高线程的复用率。 - 线程同步:使用
synchronized
关键字或ReentrantLock
来实现线程同步。 - 线程间通信:使用
wait
和notify
方法实现线程间的通信。
8. 底层核心原理
8.1 线程调度
- 时间片轮转:操作系统通过时间片轮转的方式调度线程,每个线程在分配的时间片内运行。
- 优先级:线程的优先级决定了线程被调度的频率,优先级高的线程更有可能被调度。
8.2 线程同步
- 锁机制:
synchronized
关键字和ReentrantLock
都是基于锁机制实现的线程同步。 - 监视器锁:
synchronized
关键字使用的是监视器锁(Monitor),每个对象都有一个监视器锁。
8.3 线程间通信
- 等待/通知机制:
wait
和notify
方法通过等待/通知机制实现线程间的通信。 - 条件变量:
Condition
接口提供了更灵活的等待/通知机制,可以替代wait
和notify
。
9. 示例代码
9.1 继承 Thread
类
class MyThread extends Thread {@Overridepublic void run() {System.out.println("线程 " + Thread.currentThread().getName() + " 运行中...");}
}public class ThreadExample {public static void main(String[] args) {MyThread myThread = new MyThread();myThread.start(); // 启动线程}
}
9.2 实现 Runnable
接口
class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("线程 " + Thread.currentThread().getName() + " 运行中...");}
}public class RunnableExample {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread = new Thread(myRunnable, "MyThread");thread.start(); // 启动线程}
}
9.3 实现 Callable
接口
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i < 100; i++) {sum += i;}return sum;}
}public class CallableExample {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(2);Future<Integer> future = executorService.submit(new MyCallable());try {int result = future.get(); // 获取结果System.out.println("计算结果: " + result);} catch (InterruptedException | ExecutionException e) {e.printStackTrace();} finally {executorService.shutdown(); // 关闭线程池}}
}
9.4 线程同步
public class SynchronizedMethodExample {private int count = 0;public synchronized void increment() {count++;}public static void main(String[] args) {SynchronizedMethodExample example = new SynchronizedMethodExample();Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("最终计数: " + example.count);}
}
9.5 线程间通信
public class WaitNotifyExample {private final Object lock = new Object();private boolean flag = false;public void producer() {synchronized (lock) {while (flag) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}// 生产数据System.out.println("生产数据");flag = true;lock.notify();}}public void consumer() {synchronized (lock) {while (!flag) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}// 消费数据System.out.println("消费数据");flag = false;lock.notify();}}public static void main(String[] args) {WaitNotifyExample example = new WaitNotifyExample();Thread producerThread = new Thread(() -> {for (int i = 0; i < 5; i++) {example.producer();}});Thread consumerThread = new Thread(() -> {for (int i = 0; i < 5; i++) {example.consumer();}});producerThread.start();consumerThread.start();}
}
10. 总结
本文深入探讨了 Java 线程的基础知识,包括 Thread
类、Runnable
接口、Callable
接口以及线程的生命周期,并结合大厂的最佳实践和底层核心原理,帮助读者全面掌握这些关键技术。合理地使用线程管理机制可以提高程序的性能和响应速度,避免线程安全问题。希望本文对你有所帮助,如果你有任何问题或建议,欢迎留言交流。
希望这篇文章能够满足你的需求,如果有任何进一步的问题或需要更多内容,请随时告诉我!
相关文章:
Java 编码系列:线程基础与最佳实践
引言 在多任务处理和并发编程中,线程是不可或缺的一部分。Java 提供了丰富的线程管理和并发控制机制,使得开发者可以轻松地实现多线程应用。本文将深入探讨 Java 线程的基础知识,包括 Thread 类、Runnable 接口、Callable 接口以及线程的生命…...

《深度学习》—— ResNet 残差神经网络
文章目录 一、什么是ResNet?二、残差结构(Residual Structure)三、Batch Normalization(BN----批归一化) 一、什么是ResNet? ResNet 网络是在 2015年 由微软实验室中的何凯明等几位大神提出,斩获…...

针对考研的C语言学习(定制化快速掌握重点3)
1.数组常见错误 数组传参实际传递的是数组的起始地址,若在函数中改变数组内容,数组本身也会发生变化 #include<stdio.h> void change_ch(char* str) {str[0] H; } int main() {char ch[] "hello";change_ch(ch);printf("%s\n&q…...

pikachu XXE(XML外部实体注入)通关
靶场:pikachu 环境: 系统:Windows10 服务器:PHPstudy2018 靶场:pikachu 关卡提示说:这是一个接收xml数据的api 常用的Payload 回显 <?xml version"1.0"?> <!DOCTYPE foo [ <!ENTITY …...
shell脚本定时任务通知到钉钉
shell脚本定时任务通知到钉钉 1、背景 前两天看了一下定时任务,垃圾清理、日志相关、系统巡检这些,有的服务器运行就有问题,或者不运行,正好最近在做运维标准重制运维手册,顺便把自动化这块优化一下,所…...

2.4K star的GOT-OCR2.0:端到端OCR 模型
GOT-OCR2.0是一款新一代的光学字符识别(OCR)技术,标志着人工智能在文本识别领域的重大进步。作为一款开源模型,GOT-OCR2.0不仅支持传统的文本和文档识别,还能够处理乐谱、图表以及复杂的数学公式,为用户提供…...

【JavaEE】——线程的安全问题和解决方式
阿华代码,不是逆风,就是我疯,你们的点赞收藏是我前进最大的动力!!希望本文内容能够帮助到你! 目录 一:问题引入 二:问题深入 1:举例说明 2:图解双线程计算…...

初步认识了解分布式系统
背景认识:我们要学习redis,还是得了解一下什么是分布式。为什么呢?因为redis只有在分布式系统中才能发挥它最大的作用,也就是领域展开,所以接下来我们就简单过一下什么是分布式系统 一些术语认识: &#x…...
react 为什么不能学习 vue3 进行静态节点标记优化性能?
因为 React 使用的是 JSX,而 JSX 本质上就是 JS 语言,是具有非常高的动态的,而 Vue 使用的 template 则是给了足够的约束,比如说 Vue 的 template 里面使用了很多特定的标记来做不同的事情,比如说 v-if 就是进行变量判…...

Elasticsearch黑窗口启动乱码问题解决方案
问题描述 elasticsearch启动后有乱码现象 解决方案: 提示:这里填写该问题的具体解决方案: 到 \config 文件下找到 jvm.options 文件 打开后 在文件末尾空白处 添加 -Dfile.encodingGBK 保存后重启即可。...

Logtus IT员工参加国际技术大会
Logtus IT的员工参加了国际技术大会,该大会致力于在金砖国家框架内开发俄罗斯的技术。该活动包括一个展览,俄罗斯开发商展示了他们的信息技术、电子和电信成就。展示了面向国内和国际市场(包括政府机构)的解决方案、产品和平台。 …...

ant design vue组件中table组件设置分组头部和固定总结栏
问题:遇到了个需求,不仅要设置分组的头部,还要在顶部有个统计总和的栏。 分组表头的配置主要是这个,就是套娃原理,不需要展示数据的直接写个title就行,需要展示数据的字段才需要详细的配置属性。 const co…...

2024年信息安全企业CRM选型与应用研究报告
数字化的生活给人们带来便利的同时也带来一定的信息安全隐患,如网络侵权、泄露用户隐私、黑客攻击等。在互联网高度发展的今天,信息安全与我们每个人、每个组织甚至每个国家都息息相关。 信息安全行业蓬勃发展。根据智研咨询数据,2021年&…...

【后端开发】JavaEE初阶——计算机是如何工作的???
前言: 🌟🌟本期讲解计算机工作原理,希望能帮到屏幕前的你。 🌈上期博客在这里:【MySQL】MySQL中JDBC编程——MySQL驱动包安装——(超详解) 🌈感兴趣的小伙伴看一看小编主…...
Linux(Ubuntu)源码安装postgresql16.3
文章目录 Linux(Ubuntu)源码安装postgresql016.3下载程序包编译安装软件初次执行configure错误调试1:configure: error: ICU library not found再次执行configureBuild 设置环境初始化数据库启动数据库参考 Linux(Ubuntu)源码安装…...
Python 入门教程(7)面向对象 | 7.6、多态
文章目录 一、多态1、鸭子类型2、实现多态的机制2.1、鸭子类型2.2、继承与重写 3、Python多态的优势4、总结 前言: 在面向对象编程(OOP)中,多态(Polymorphism)是一种非常重要的概念,多态就是同一…...

Cilium + ebpf 系列文章-什么是ebpf?(一)
前言: 这篇非常非常干,很有可能读不懂。 这里非常非常推荐,建议使用Cilium官网的lab来辅助学习!!!Resources Library - IsovalentExplore Isovalents Resource Library, your one-stop destination for ins…...

RabbitMQ08_保证消息可靠性
保证消息可靠性 一、生产者可靠性1、生产者重连机制(防止网络波动)2、生产者确认机制Publisher Return 确认机制Publisher Confirm 确认机制 二、MQ 可靠性1、数据持久化交换机、队列持久化消息持久化 2、Lazy Queue 惰性队列 三、消费者可靠性1、消费者…...

恶意Bot流量识别分析实践
1、摘要 随着互联网的发展,自动化工具和脚本(Bots)的使用越来越普遍。虽然一些善意 Bots 对于网站的正常运行和数据采集至关重要,但恶意 Bots 可能会对网站带来负面影响,如爬取敏感信息、恶意注册、刷流量等。因此&am…...
Java2 实用教程(第6版)习题2 第四题
【源文件的命名与书中的不同】 四、阅读程序题 1、上机运行下列程序,注意观察输出的结果。 public class E2_1 {public static void main(String args[]){for(int i20302;i<20322;i){System.out.println((char) i);}} } 运行结果: 低 住 佐 佑 佒…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...

R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化
iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...