Java并发编程实战 Day 1:Java并发编程基础与线程模型

article/2025/6/24 0:00:06

【Java并发编程实战 Day 1】Java并发编程基础与线程模型

开篇:系列定位与学习目标

欢迎来到为期30天的《Java并发编程实战》系列教程。本系列将从Java并发编程的基础知识讲起,逐步深入到高级特性与实战应用,帮助开发者构建高性能、可扩展的并发系统。

作为开篇第一天,我们将聚焦于Java并发编程基础与线程模型,重点包括以下内容:

  • 线程的基本概念和作用
  • 如何创建和启动线程
  • 线程的生命周期及其状态转换
  • 线程组(ThreadGroup)的使用
  • 实际应用场景与性能优化建议

通过今天的学习,您将掌握如何在Java中高效地管理多线程任务,并为后续更复杂的并发编程打下坚实基础。

理论基础:线程与并发的基本概念

什么是线程?

线程是操作系统调度的最小单元,一个进程中可以包含多个线程,每个线程都有独立的执行路径。Java语言内置了对多线程的支持,使得开发者可以轻松实现并发程序。

进程与线程的区别
特性进程线程
资源分配独立内存空间共享进程资源
通信方式进程间通信(IPC)直接共享变量
创建开销较大较小
切换效率

Java中的线程模型

Java采用的是抢占式线程调度模型,即由操作系统决定哪个线程获得CPU时间片。Java线程映射到操作系统的原生线程上,因此其行为受JVM和底层操作系统共同影响。

JVM层面的线程实现

在HotSpot虚拟机中,Java线程是通过java.lang.Thread类来表示的。每个Thread实例对应一个操作系统级别的线程。当调用start()方法时,JVM会创建并启动一个新的原生线程。

public class ThreadCreationExample {public static void main(String[] args) {// 方法一:继承Thread类Thread t1 = new Thread() {@Overridepublic void run() {System.out.println("线程t1正在运行...");}};t1.start();// 方法二:实现Runnable接口Runnable task = () -> {System.out.println("线程t2正在运行...");};Thread t2 = new Thread(task);t2.start();// 方法三:使用Lambda表达式Thread t3 = new Thread(() -> System.out.println("线程t3正在运行..."));t3.start();}
}

适用场景:为什么需要多线程?

高性能计算需求

现代应用程序往往需要处理大量并发请求,如Web服务器、数据库连接池等。单线程无法充分利用多核CPU资源,而多线程可以通过并行处理显著提高程序吞吐量。

用户界面响应

GUI应用(如Swing或JavaFX)通常需要保持界面流畅,避免阻塞主线程。例如,在下载文件时,可以在后台线程中执行网络请求,同时更新进度条而不影响用户交互。

IO密集型任务

对于涉及磁盘读写、网络通信等IO密集型任务,多线程可以帮助我们更好地利用等待时间。例如,批量处理日志文件时,可以使用多个线程并行读取不同文件。

代码实践:线程创建与管理

创建线程的三种方式

  1. 继承Thread类:直接定义一个继承自Thread的子类,并重写run()方法。
  2. 实现Runnable接口:将任务逻辑封装在Runnable对象中,适用于任务与线程分离的设计。
  3. 使用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;public class CallableExample {public static void main(String[] args) {ExecutorService executor = Executors.newSingleThreadExecutor();Callable<String> task = () -> {return "Hello from callable thread!";};Future<String> future = executor.submit(task);try {String result = future.get();System.out.println("任务结果: " + result);} catch (InterruptedException | ExecutionException e) {e.printStackTrace();} finally {executor.shutdown();}}
}

线程命名与优先级

合理设置线程名称有助于调试和日志记录,而线程优先级则会影响调度器对线程的选择。

Thread namedThread = new Thread(() -> {System.out.println("当前线程名称: " + Thread.currentThread().getName());
});
namedThread.setName("Worker-Thread");
namedThread.setPriority(Thread.MAX_PRIORITY); // 设置为最高优先级(10)
namedThread.start();

守护线程(Daemon Thread)

守护线程是一种“后台服务线程”,当所有非守护线程结束时,JVM会自动退出。

Thread daemonThread = new Thread(() -> {while (true) {System.out.println("守护线程正在运行...");try {Thread.sleep(1000);} catch (InterruptedException e) {break;}}
});
daemonThread.setDaemon(true);
daemonThread.start();

实现原理:线程状态与生命周期

线程状态详解

Java线程有6种基本状态:

  • NEW:线程刚被创建,尚未启动
  • RUNNABLE:线程正在JVM中执行(可能在等待操作系统调度)
  • BLOCKED:线程因等待监视器锁而阻塞
  • WAITING:无限期等待其他线程通知
  • TIMED_WAITING:有限时间内等待其他线程通知
  • TERMINATED:线程已完成或异常终止

状态转换流程图

在这里插入图片描述

获取线程状态

Thread.State state = Thread.currentThread().getState();
System.out.println("当前线程状态: " + state);

性能测试:线程数量对性能的影响

为了评估线程数量对性能的影响,我们可以编写一个简单的压力测试程序。

测试环境配置

  • CPU:Intel i7-9750H
  • 内存:16GB DDR4
  • JDK版本:OpenJDK 17

测试代码

import java.util.ArrayList;
import java.util.List;public class ThreadPerformanceTest {private static final int THREAD_COUNT = 1000;private static final int TASK_COUNT = 10000;public static void main(String[] args) throws InterruptedException {List<Thread> threads = new ArrayList<>();for (int i = 0; i < THREAD_COUNT; i++) {Thread t = new Thread(() -> {for (int j = 0; j < TASK_COUNT; j++) {// 模拟简单计算Math.sqrt(j);}});threads.add(t);t.start();}for (Thread t : threads) {t.join();}System.out.println("所有线程已完成!");}
}

测试结果对比

线程数平均耗时(ms)CPU利用率内存占用(MB)
100180045%150
500210060%280
1000250070%420
2000310080%700

结论:随着线程数增加,CPU利用率提高,但线程切换开销也增大,导致整体性能增长放缓。

最佳实践:线程管理策略

使用线程池代替手动创建线程

频繁创建和销毁线程会导致额外开销,推荐使用ExecutorService来管理线程池。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolExample {public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(10);for (int i = 0; i < 100; i++) {final int taskId = i;executor.submit(() -> {System.out.println("执行任务ID: " + taskId);});}executor.shutdown();}
}

避免线程泄漏

确保所有线程最终都能正常结束,避免长时间运行的线程占用资源。对于守护线程,应谨慎使用,防止误杀关键服务。

合理设置线程优先级

虽然线程优先级不能保证绝对顺序,但在某些场景下(如实时数据处理),适当调整优先级可以改善响应速度。

Thread highPriorityThread = new Thread(() -> {while (true) {// 执行关键任务}
});
highPriorityThread.setPriority(Thread.MAX_PRIORITY);
highPriorityThread.start();

案例分析:电商秒杀系统的并发控制

问题背景

某电商平台在促销期间面临每秒数万次的抢购请求,传统单线程处理模式已无法满足高并发需求。

解决方案

  1. 异步处理订单:将下单操作拆分为接收请求和持久化两个阶段,前者使用高并发线程处理,后者放入队列异步执行。
  2. 限制最大并发数:通过信号量(Semaphore)控制同时处理请求的最大线程数,防止系统过载。
  3. 线程池隔离:为不同类型的任务(如支付、库存扣减)分配独立线程池,避免相互干扰。
代码示例:基于线程池的秒杀系统优化
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;public class SeckillSystem {private static final int MAX_CONCURRENT_USERS = 100;private static final Semaphore semaphore = new Semaphore(MAX_CONCURRENT_USERS);private static final ExecutorService executor = Executors.newFixedThreadPool(20);public static void main(String[] args) {for (int i = 0; i < 1000; i++) {final int userId = i;executor.submit(() -> {try {semaphore.acquire();processSeckill(userId);} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {semaphore.release();}});}executor.shutdown();}private static void processSeckill(int userId) {// 模拟业务逻辑System.out.println("用户" + userId + "正在参与秒杀...");try {Thread.sleep(50);} catch (InterruptedException e) {Thread.currentThread().interrupt();}System.out.println("用户" + userId + "秒杀成功!");}
}

总结:关键知识点回顾与展望

今天我们学习了Java并发编程的基础知识,包括线程的创建、生命周期管理以及线程组的使用。通过实际代码演示了如何高效地管理多线程任务,并探讨了线程池、优先级设置等最佳实践。

核心技能总结

  • 掌握了三种创建线程的方式及其适用场景
  • 理解了线程状态及其转换机制
  • 学会了如何使用线程池优化并发性能
  • 实践了线程优先级设置与守护线程的应用

下一步学习预告

明天我们将深入探讨synchronized关键字的工作原理,包括对象锁、类锁的区别以及JVM内部是如何实现同步机制的。敬请期待!

参考资料

  1. Oracle官方文档 - Java Threads
  2. Java并发编程实战(Brian Goetz)
  3. Understanding the Java Thread Life Cycle
  4. Effective Java - Joshua Bloch
  5. Java Concurrency in Practice - Brian Goetz et al.

核心技能与实际应用

通过今天的学习,您已经掌握了以下实用技能:

  • 能够根据业务需求选择合适的线程创建方式
  • 理解线程状态变化,能够有效监控和管理线程生命周期
  • 使用线程池提升系统并发性能,减少资源消耗
  • 在实际项目中合理运用守护线程和优先级设置

这些技能可以直接应用于各种高并发场景,如Web服务器优化、大数据处理、实时系统开发等领域。希望您能将所学知识灵活运用于工作中,打造更加高效稳定的并发系统!


http://www.hkcw.cn/article/OuqamlytaL.shtml

相关文章

如何配置国内docker镜像源?

如何配置国内docker镜像源&#xff1f; 安装docker chattr -i /etc/passwd chattr -i /etc/shadow chattr -i /etc/group chattr -i /etc/gshadow# 下载slirp4netns rpm包 wget https://1407433742.rsc.cdn77.org/c7-extras.x86_64/slirp4netns/20200428221211/0.4.3-4.el7_…

【Ubuntu】摸鱼技巧之虚拟机环境复制

前言 提示&#xff1a;所有的操作都需要关闭虚拟机 如何快速在其它电脑布置&#xff0c;linux环境&#xff0c;如果我们有一个环境直接拷贝就有时间摸鱼呀。 1.直接复制简单粗暴 不做赘述&#xff0c;如果不会复制&#xff0c;那么请右击鼠标压缩复制 2.克隆虚拟机 2.1 …

C51单片机

1.单片机的概述 (1)微处理器(CPU) 运算器主要负责数据的算术运算和逻辑运算。 控制器:是发布命令的“决策机构”&#xff0c;负责协调和指挥整个计算机系统操作。 (2)存储器 程序存储器:用于存储程序和一些固定不变的常数和表格数据&#xff0c;一般由只读存储器(ROM)组成。…

介绍一种LDPC码译码器

介绍比特翻转译码原理以及LDPC码译码器的设计。 1 译码理论 比特翻转&#xff08;BF&#xff09;译码算法是硬判决算法的一种。 主要译码思想是&#xff1a;当有一个校验矩阵出错时&#xff0c;BF 算法认为在这个校验矩阵中一定至少存在一个位置的码字信息是错误的&#xff1…

Python简易音乐播放器开发教程

&#x1f4da; 前言 编程基础第一期《12-30》–音乐播放器是日常生活中常用的应用程序&#xff0c;使用Python和pygame库可以轻松实现一个简易的音乐播放器。本教程将详细讲解如何开发一个具有基本功能的音乐播放器&#xff0c;并解析其中涉及的Python编程知识点。 &#x1f6e…

【Docker项目实战篇】Docker部署PDF查看器PdfDing

【Docker项目实战篇】Docker部署PDD查看器PdfDing 一、PdfDing介绍1.1 PdfDing简介1.2 PdfDing主要特点1.3 主要使用场景 二、本次实践规划2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载Pd…

AE 脚本表达式错误 Default ColorSelectionwhile (true){ break;} }

这个问题卡了我挺久的&#xff0c; 都没有解决&#xff0c; 暂时放在这&#xff0c;有解决办法来写。 也希望看到的朋友能帮忙解答

MyBatis01

目录 一、Mybatis 1.1 什么是 MyBatis&#xff1f; 1.2 ORM思想 Hibernate Mybatis&#xff08;ibatis&#xff09; 持久层技术对比 二、mybatis基础操作流程 2.1 引入jar包 2.2 mybatis核心配置文件 2.3 java文件和sql文件相分离 2.4 mybatis框架核心类&#xff0c…

C56-亲自实现字符串拷贝函数

一 strcpy简介 功能&#xff1a;将源字符串&#xff08;包括 \0&#xff09;复制到目标地址。 原型&#xff1a; char *strcpy(char *dest, const char *src);参数&#xff1a; dest&#xff1a;目标地址&#xff08;需足够大&#xff09;。src&#xff1a;源字符串&#xf…

设计模式——简单工厂模式(创建型)

摘要 本文主要介绍了简单工厂模式&#xff0c;包括其定义、结构、实现方式、适用场景、实战示例以及思考。简单工厂模式是一种创建型设计模式&#xff0c;通过工厂类根据参数决定创建哪一种产品类的实例&#xff0c;封装了对象创建的细节&#xff0c;使客户端无需关心具体类的…

山东大学软件学院项目实训-基于大模型的模拟面试系统-面试官和面试记录的分享功能(2)

本文记录在发布文章时&#xff0c;可以添加自己创建的面试官和面试记录到文章中这一功能的实现。 前端 首先是在原本的界面的底部添加了两个多选框&#xff08;后期需要美化调整&#xff09; 实现的代码&#xff1a; <el-col style"margin-top: 1rem;"><e…

【Hot 100】121. 买卖股票的最佳时机

目录 引言买卖股票的最佳时机我的解题 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;算法专栏&#x1f4a5; 标题&#xff1a;【Hot 100】121. 买卖股票的最佳时机❣️ 寄语&#xff1a;书到用时方恨少&#xff0c;事非经过不知难&#xff01; 引…

《Spring Cloud Gateway 快速入门:从路由到自定义 Filter 的完整教程》​

1.网关介绍 在前面的学习中&#xff0c;我们通过Eureka和Nacos解决了辅助注册&#xff0c;使用Spring Cloud LoadBalance解决了负载均衡的问题&#xff0c;使用OpenFeign解决了远程调用的问题。 但是当前的所有微服务的接口都是直接对外暴露的&#xff0c;外部是可以直接访问…

高并发抽奖系统优化方案

引子 最近接触了一个抽奖的项目&#xff0c;由于用户量比较大&#xff0c;而且第三方提供的认证接口并发量有限&#xff0c;为了保证服务的高可用性&#xff0c;所以对高并限制发有一定的要求。经过一系列研究和讨论&#xff0c;做出了以下一些优化方案。 需求分析 根据用户量…

【Doris基础】Apache Doris中的Segment详解:存储与查询的核心组件

目录 1 Segment概述 1.1 什么是Segment 1.2 Segment的重要性 2 Segment的物理结构 2.1 Segment文件组成 2.2 列数据存储格式 3 Segment的生命周期 3.1 Segment创建 3.2 Segment合并(Compaction) 3.3 Segment过期与清理 4 Segment与查询处理 4.1 查询中的Segment剪枝…

JAVA中的枚举

代码示例 理论 枚举的行为多态 public enum MyLockStrategy {SKIP_FAST(){Overridepublic boolean tryLock(RLock lock, MyLock prop) throws InterruptedException {return lock.tryLock(0, prop.leaseTime(), prop.unit());}},FAIL_AFTER_RETRY_TIMEOUT(){Overridepublic bo…

电脑驱动程序更新工具, 3DP Chip 中文绿色版,一键更新驱动!

介绍 3DP Chip 是一款免费的驱动程序更新工具&#xff0c;可以帮助用户快速、方便地识别和更新计算机硬件驱动程序。 驱动程序更新工具下载 https://pan.quark.cn/s/98895d47f57c 软件截图 软件特点 简单易用&#xff1a;用户界面简洁明了&#xff0c;操作方便&#xff0c;…

day61—DFS—省份数量(LeetCode-547)

题目描述 有 n 个城市&#xff0c;其中一些彼此相连&#xff0c;另一些没有相连。如果城市 a 与城市 b 直接相连&#xff0c;且城市 b 与城市 c 直接相连&#xff0c;那么城市 a 与城市 c 间接相连。 省份 是一组直接或间接相连的城市&#xff0c;组内不含其他没有相连的城市…

MySql(十)

目录 准备工作 1&#xff09;准备一张表 2&#xff09;插入数据 1.排序 1--asc 升序 2--desc 降序 3--组合排序 2.聚合函数 1.count(字段名) 2.sum(字段名) 3.max(字段名) 4.min(字段名) 5.avg(字段名) 准备工作 1&#xff09;准备一张表 CREATE table role(roleid INT PRIMAR…

LabVIEW Val (Sgnl) 属性

在 LabVIEW 事件驱动架构中&#xff0c;Val (Sgnl) 属性&#xff08;Value (Signaling)&#xff09;是实现编程触发与用户交互行为一致性的关键技术。与普通 Value 属性不同&#xff0c;Val (Sgnl) 在修改控件值的同时强制生成值改变事件&#xff0c;确保程序逻辑与 UI 交互保持…