Java多线程:从基础到实战

引言

多线程是Java并发编程的核心技术之一,广泛应用于服务器开发、数据处理、实时系统等领域。通过多线程,程序可以充分利用CPU资源,提高执行效率,同时处理多个任务。本文将从多线程的基本概念实现方式线程状态同步与通信常见问题与解决方案,深入解析Java多线程的原理与实践。


一、多线程的核心概念

1. 什么是线程?

线程是操作系统能够进行运算调度的最小单位。一个进程可以包含多个线程,它们共享进程的资源(如内存),但每个线程有独立的执行路径。

2. 多线程的优势

  • 提高程序性能:通过并行执行多个任务,充分利用多核CPU。
  • 异步处理:避免主线程阻塞(如GUI应用、网络请求)。
  • 资源共享:线程间可直接共享进程的内存数据(需注意线程安全)。

3. 多线程与并发的区别

  • 并发:多个任务交替执行,逻辑上“同时”进行(如单核CPU的多任务调度)。
  • 并行:多个任务真正同时执行(如多核CPU的多线程协作)。

二、Java多线程的实现方式

1. 继承 Thread 类

class MyThread extends Thread {@Overridepublic void run() {System.out.println("线程执行:" + Thread.currentThread().getName());}
}
// 启动线程
new MyThread().start();

缺点:Java单继承限制,无法继承其他类。

2. 实现 Runnable 接口(推荐)

class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("线程执行:" + Thread.currentThread().getName());}
}
// 启动线程
new Thread(new MyRunnable()).start();

优点:避免单继承限制,便于资源共享(如多个线程操作同一实例)。

3. 使用 Callable 接口(带返回值)

class MyCallable implements Callable<String> {@Overridepublic String call() throws Exception {return "结果:" + Thread.currentThread().getName();}
}
// 提交任务
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(new MyCallable());
System.out.println(future.get());
executor.shutdown();

4. 线程池(推荐)

线程池管理线程复用,避免频繁创建销毁线程:

ExecutorService pool = Executors.newFixedThreadPool(5); // 固定大小线程池
pool.execute(() -> System.out.println("任务执行"));
pool.shutdown();

三、线程的生命周期与状态

Java线程有以下5种基本状态(JDK 1.8):

状态说明
新建(New)创建线程对象后,调用 start() 之前的状态。
就绪(Runnable)调用 start() 后,等待CPU调度执行。
运行(Running)CPU开始调度线程,执行 run() 方法。
阻塞(Blocked)线程因等待锁、I/O、休眠等原因暂时停止。
终止(Terminated)线程执行完毕或抛出异常退出。

阻塞状态细分

  1. 等待阻塞:调用 wait(),进入等待池(需 notify() 唤醒)。
  2. 同步阻塞:未获取到 synchronized 锁。
  3. 其他阻塞:调用 sleep()join() 或I/O操作。

四、线程同步与线程安全

1. 什么是线程安全?

当多个线程并发访问共享资源时,程序仍能正确运行(无数据错误、状态混乱)。

2. 常见问题与解决方案

(1)原子性问题
  • 问题:非原子操作(如 count++)被多个线程拆分执行。
  • 解决方案
    • synchronized 关键字
      public synchronized void increment() {count++;
      }
    • ReentrantLock 显式锁
      ReentrantLock lock = new ReentrantLock();
      public void increment() {lock.lock();try {count++;} finally {lock.unlock();}
      }
    • 原子类(AtomicXXX)
      AtomicInteger atomicCount = new AtomicInteger(0);
      atomicCount.incrementAndGet(); // 线程安全的自增
(2)可见性问题
  • 问题:线程A修改变量后,线程B未及时读取到最新值。
  • 解决方案
    • volatile 关键字
      private volatile boolean flag = false; // 禁止指令重排序,保证可见性
(3)有序性问题
  • 问题:编译器或CPU优化导致指令顺序变化。
  • 解决方案
    • synchronized / volatile:通过内存屏障确保顺序。

五、线程通信:生产者-消费者模型

经典场景

  • 生产者:每秒生成一个产品,交给店员。
  • 消费者:每秒购买一个产品。
  • 店员:最多管理20个产品。

实现代码

class Clerk {private int productCount = 0;private static final int MAX_CAPACITY = 20;// 生产产品public synchronized void produce() throws InterruptedException {while (productCount >= MAX_CAPACITY) {System.out.println(Thread.currentThread().getName() + " 库存已满,等待...");wait();}productCount++;System.out.println(Thread.currentThread().getName() + " 生产了1个产品,库存:" + productCount);notifyAll();}// 消费产品public synchronized void consume() throws InterruptedException {while (productCount <= 0) {System.out.println(Thread.currentThread().getName() + " 没有产品,等待...");wait();}productCount--;System.out.println(Thread.currentThread().getName() + " 购买了1个产品,库存:" + productCount);notifyAll();}
}// 生产者
class Producer implements Runnable {private Clerk clerk;public Producer(Clerk clerk) { this.clerk = clerk; }@Overridepublic void run() {while (true) {try {clerk.produce();Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();break;}}}
}// 消费者
class Customer implements Runnable {private Clerk clerk;public Customer(Clerk clerk) { this.clerk = clerk; }@Overridepublic void run() {while (true) {try {clerk.consume();Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();break;}}}
}

六、多线程避坑指南

1. 死锁问题

死锁条件

  • 互斥:资源不可共享。
  • 请求与保持:线程持有资源并申请新资源。
  • 不可剥夺:资源只能由持有线程释放。
  • 循环等待:多个线程形成资源环。

解决方案

  • 按固定顺序加锁:避免循环等待。
  • 超时机制:使用 tryLock() 设置超时时间。
  • 资源监控:通过 jstack 分析死锁。

2. 线程池使用不当

错误示例

void realJob() {ThreadPoolExecutor exe = new ThreadPoolExecutor(...); // 每次请求新建线程池exe.submit(new Runnable() {...});
}

后果:线程池爆炸,耗尽系统资源。

正确做法

  • 将线程池作为静态变量复用。
  • 使用 Executors 工厂方法(如 newFixedThreadPool)。

七、总结

Java多线程是提升程序性能的关键技术,但也伴随着复杂的并发问题。掌握以下核心要点:

  1. 线程创建与启动:优先使用 Runnable 和线程池。
  2. 线程同步synchronizedReentrantLock、原子类。
  3. 线程通信wait()/notify() 实现生产者-消费者模型。
  4. 避免死锁:按顺序加锁、超时机制。
  5. 线程池管理:避免资源耗尽,复用线程。

通过合理设计和实践,多线程可以成为构建高性能、高可靠系统的重要工具。

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

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

相关文章

list集合可以一边遍历一遍修改元素吗?

今天看来一下Java中list集合部分的八股&#xff0c;发现了一个以前没注意过的问题&#xff0c;记录一下list可以一边遍历一边修改元素吗&#xff1f;答&#xff1a;在 Java 中&#xff0c;List在遍历过程中是否可以修改元素取决于遍历方式和具体的List实现类。①&#xff1a;对…

Infusing fine-grained visual knowledge to Vision-Language Models

Infusing fine-grained visual knowledge to Vision-Language Models Authors: Nikolaos-Antonios Ypsilantis, Kaifeng Chen, Andr Araujo, Ondřej Chum Deep-Dive Summary: 视觉-语言模型中注入细粒度视觉知识 摘要 大规模对比预训练产生了强大的视觉-语言模型&#xf…

RK3576赋能无人机巡检:多路视频+AI识别引领智能化变革

随着工业巡检任务的复杂度不断提升&#xff0c;无人机逐渐取代传统人工&#xff0c;成为电力、能源、林业、农业等行业的“高空作业主力”。然而&#xff0c;巡检并非简单的拍摄和回放&#xff0c;它要求无人机实时采集多路画面、快速分析异常&#xff0c;并稳定回传数据。这对…

ollama Modelfile 文件生成

输入 根据如下TEMPLATE和params写一个modelfile文件&#xff0c;TEMPLATE为&#xff1a;{{- $lastUserIdx : -1 -}} {{- range $idx, $msg : .Messages -}} {{- if eq $msg.Role “user” }}{{ $lastUserIdx $idx }}{{ end -}} {{- end }} {{- if or .System .Tools }}<|i…

关联规则挖掘2:FP-growth算法(Frequent Pattern Growth,频繁模式增长)

目录 一、核心思想&#xff1a;一个形象的比喻 二、核心思想的具体拆解 步骤一&#xff1a;构建FP-tree&#xff08;频繁模式树&#xff09; 步骤二&#xff1a;从FP-tree中挖掘频繁项集 为什么这很高效&#xff1f; 三、总结 核心思想与优势 适用场景与缺点 四、例题…

在IDEA中DEBUG调试时查看MyBatis-Plus动态生成的SQL语句

在IDEA中DEBUG调试时查看MyBatis-Plus动态生成的SQL语句前言&#xff1a;动态SQL调试的痛与解决方案一、准备工作&#xff1a;调试前的检查清单二、基础方法&#xff1a;SqlSessionTemplate断点调试步骤1&#xff1a;定位SqlSessionTemplate类步骤2&#xff1a;在invoke方法上设…

Linux 文本处理三剑客:awk、grep、sed 完全指南

Linux 文本处理三剑客&#xff1a;awk、grep、sed 完全指南 1. 概述 Linux 系统提供了三个强大的文本处理工具&#xff1a;awk、grep 和 sed&#xff0c;它们各有所长&#xff0c;结合使用可以高效地处理文本数据。 awk&#xff1a;擅长文本分析和格式化输出&#xff0c;是一…

pyecharts可视化图表组合组件_Grid:打造专业数据仪表盘

pyecharts可视化图表组合组件_Grid&#xff1a;打造专业数据仪表盘 目录pyecharts可视化图表组合组件_Grid&#xff1a;打造专业数据仪表盘引言图表1&#xff1a;Grid-Overlap-多X/Y轴示例代码解析1. 图表创建2. 多轴配置3. 图表重叠4. Grid布局效果与应用图表2&#xff1a;Gri…

【电气工程学习】

三极管中&#xff1a;集电极C,基极B&#xff0c;发射极E接线&#xff1a;棕正蓝负黑信号NPN开关输出的是我们的0V,也叫低电平PNP开关输出的是24V,也就是高电平&#xff08;NPN开关导通时&#xff0c;相当于把输出端“拉”到0V&#xff08;低电平&#xff09;&#xff0c;称为“…

【嵌入式】CAN通信

CAN 总线最初由博世于1980年代为汽车行业开发&#xff0c;能够简化复杂的布线网络&#xff0c;还确保可靠和安全的数据传输。 1.CAN技术解释 CAN网络中的每个节点&#xff0c;都是平等的&#xff0c;没有主次之分&#xff0c;这一点和SPI和I2C不同。每个节点都可以在需要的时…

Apache ShenYu网关与Nacos的关联及如何配合使用

Apache ShenYu 网关与 Nacos 之间的关系可以概括为 “协作互补”:Nacos 作为 服务注册与配置中心,为 ShenYu 提供动态的服务发现和配置管理能力,而 ShenYu 作为 流量网关,依赖 Nacos 实现路由信息的动态更新和实时生效。以下是详细解析: 1. 核心关系图解 拉取服务列表/路…

【CPP】一个CPP的Library(libXXXcore)和测试程序XXX_main的Demo

一个CPP的Library和测试程序Demo 1. 思路描述 目录结构 总控CMakeList.txt文件 2. Library代码实现 2.1 XXXLib.hpp文件(对外的接口定义文件)和XXXLib.cpp文件 2.1.1 XXXLib.hpp文件 2.1.2 XXXLib.cpp文件 2.2 CXXXLibApi.hpp文件和CXXXLibApi.cpp文件(内部的API基类) 2.2.1 CX…

【YashanDB认证】学习YashanDB的探索之路:从入门到实践

在国产数据库蓬勃发展的浪潮中&#xff0c;选择了YashanDB作为技术学习的切入点。这不仅让我深入了解了数据库的核心技术&#xff0c;也让我深刻体会到国产数据库在性能、可靠性和生态适配上的创新价值。以下是我在学习YashanDB过程中的经验与感悟。 一、YashanDB基础介绍 Ya…

element UI 和 element plus 在组件上有哪些不同

Element UI 和 Element Plus 都是基于 Vue 的桌面端 UI 组件库&#xff0c;由同一团队&#xff08;饿了么前端团队&#xff09;开发和维护。Element Plus 是 Element UI 的升级版&#xff0c;专为 Vue 3 设计&#xff0c;而 Element UI 仅支持 Vue 2。以下是它们在组件层面的主…

【3D重建技术】如何基于遥感图像和DEM等数据进行城市级高精度三维重建?

城市级高精度三维重建是融合多源空间数据&#xff08;遥感图像、DEM、GIS矢量等&#xff09;、计算机视觉与地理信息处理技术的复杂过程&#xff0c;核心目标是构建包含“地形地物&#xff08;建筑、道路、植被等&#xff09;”的真实、高精度三维场景。其流程可分为数据准备、…

【unitrix数间混合计算】3.4 无符号小数部分标记trait(bin_unsigned.rs)

一、源码 这段代码定义了一个类型级二进制小数系统&#xff0c;用于在编译时表示和验证二进制小数部分的有效性。 use crate::number::{F0, BFrac, Bit};/// 标记合法的二进制小数部分类型 pub trait BinFrac: Copy Default static {}// 空小数部分&#xff08;表示值为0&…

从一次 DDoS 的“死亡回放”看现代攻击链的进化

本文记录的是作者上周在测试环境真实踩到的坑。为了让读者能复现并亲手体验防御思路&#xff0c;文末给出了一份最小可运行的 Go 脚本&#xff0c;支持本地压测 日志回放&#xff0c;方便对比加防护前后的差异。攻击现场还原 周一凌晨 2:14&#xff0c;监控群里突然弹出告警&a…

LeetCode热题100--101. 对称二叉树--简单

1. 题目 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a;输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true 示例 2&#xff1a;输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;false 2. 题解 /*** Definition for…

Pub/Sub是什么意思

Pub/Sub&#xff08;发布/订阅模式&#xff09;​​ 是一种异步消息通信范式&#xff0c;用于分布式系统中不同组件之间的解耦通信。它的核心思想是将消息的发送方&#xff08;发布者&#xff09;​​ 和接收方&#xff08;订阅者&#xff09;​​ 分离&#xff0c;通过一个中间…

Redisson3.14.1及之后连接阿里云redis代理模式,使用分布式锁:ERR unknown command ‘WAIT‘

文章目录一、问题背景1、问题原因2、阿里云对Redisson的支持二、解决方案1、继续使用Redisson3.14.0版本2、阿里云redis改为直连模式3、升级Redisson版本到 3.47.0一、问题背景 1、问题原因 阿里云Redis分直连和代理模式&#xff0c;其中代理模式是不支持WAIT命令的。 目前尝…