轻松掌握Java多线程 - 第一章:多线程入门
cac55 2025-05-07 23:02 8 浏览 0 评论
学习目标
- 理解线程与多线程的基本概念
- 掌握为什么要使用多线程编程的主要原因
- 学习Java中实现多线程的两种基本方式
- 创建并运行你的第一个多线程程序
1. 什么是线程与多线程
1.1 线程的概念
线程是操作系统能够进行运算调度的最小单位,也是程序执行流的最小单位。简单来说,线程就是一个单独的执行路径,它可以独立执行特定的代码片段。
提示: 可以把线程比作是一条流水线上的工人,每个工人负责完成自己的工作。多个线程就像多个工人同时工作,提高了效率。
在Java中,当我们运行一个Java程序时,JVM会创建一个主线程来执行main()方法。这个主线程就是程序默认的执行路径。
package org.devlive.tutorial.multithreading.chapter01;
/**
* 演示主线程的基本概念
*/
public class MainThreadDemo
{
public static void main(String[] args)
{
// 获取当前线程(主线程)
Thread mainThread = Thread.currentThread();
// 打印主线程信息
System.out.println("当前执行的线程名称:" + mainThread.getName());
System.out.println("线程ID:" + mainThread.getId());
System.out.println("线程优先级:" + mainThread.getPriority());
System.out.println("线程是否为守护线程:" + mainThread.isDaemon());
System.out.println("线程状态:" + mainThread.getState());
}
}
运行上面的代码,你会看到类似这样的输出:
当前执行的线程名称:main
线程ID:1
线程优先级:5
线程是否为守护线程:false
线程状态:RUNNABLE
1.2 多线程的概念
多线程是指在一个程序中同时运行多个线程,每个线程可以执行不同的任务,且线程之间可以并发执行。在传统的单线程程序中,任务是按顺序一个接一个地执行的,而在多线程程序中,多个任务可以看起来像是同时执行的。
提示: 在单核CPU上,多线程通过时间片轮转实现"伪并行";在多核CPU上,多线程可以实现真正的并行执行。
2. 为什么需要多线程编程
在实际开发中,多线程编程有很多优势:
2.1 提高CPU利用率
现代计算机通常有多个CPU核心,单线程程序只能使用一个核心,而多线程程序可以充分利用多核心资源,提高CPU的利用率。
2.2 提高程序响应性
在GUI应用程序中,如果所有操作都在一个线程中进行,那么当执行耗时操作时,整个界面会卡住无法响应用户操作。通过将耗时操作放在单独的线程中执行,可以保持界面的响应性。
2.3 更好的资源利用
当一个线程因为I/O操作(如读写文件、网络通信)而阻塞时,CPU可以切换到其他线程继续执行,提高整体的资源利用效率。
2.4 简化复杂问题的处理
有些问题天然适合使用多线程处理,比如服务器同时处理多个客户端请求,或者并行处理大量数据。
下面我们来看一个简单例子,直观感受单线程和多线程的区别:
package org.devlive.tutorial.multithreading.chapter01;
/**
* 单线程与多线程计算对比
*/
public class MultiThreadAdvantageDemo
{
// 执行大量计算的方法
private static void doHeavyCalculation(String threadName)
{
System.out.println(threadName + " 开始计算...");
long sum = 0;
for (long i = 0; i < 3_000_000_000L; i++) {
sum += i;
}
System.out.println(threadName + " 计算完成,结果:" + sum);
}
public static void main(String[] args)
{
long startTime = System.currentTimeMillis();
// 单线程执行两次计算
System.out.println("===== 单线程执行 =====");
doHeavyCalculation("计算任务1");
doHeavyCalculation("计算任务2");
long endTime = System.currentTimeMillis();
System.out.println("单线程执行总耗时:" + (endTime - startTime) + "ms");
// 多线程执行两次计算
System.out.println("\n===== 多线程执行 =====");
startTime = System.currentTimeMillis();
// 创建两个线程分别执行计算任务
Thread thread1 = new Thread(() -> doHeavyCalculation("线程1"));
Thread thread2 = new Thread(() -> doHeavyCalculation("线程2"));
// 启动线程
thread1.start();
thread2.start();
// 等待两个线程执行完成
try {
thread1.join();
thread2.join();
}
catch (InterruptedException e) {
e.printStackTrace();
}
endTime = System.currentTimeMillis();
System.out.println("多线程执行总耗时:" + (endTime - startTime) + "ms");
}
}
在多核CPU的电脑上运行这段代码,你会发现多线程执行的总时间明显少于单线程执行的总时间,这就是多线程并行计算的优势。
3. Java中实现多线程的两种基本方式
Java提供了两种基本的方式来创建线程:继承Thread类和实现Runnable接口。
3.1 继承Thread类
通过继承Thread类并重写其run()方法来创建一个新的线程类:
package org.devlive.tutorial.multithreading.chapter01;
/**
* 通过继承Thread类实现多线程
*/
public class ThreadExtendsDemo
{
// 自定义线程类,继承Thread类
static class MyThread
extends Thread
{
private final String message;
public MyThread(String message)
{
this.message = message;
}
// 重写run方法,定义线程执行的任务
@Override
public void run()
{
// 打印信息,显示当前线程名称
for (int i = 0; i < 5; i++) {
System.out.println(getName() + " 执行: " + message + " - 第" + (i + 1) + "次");
try {
// 线程休眠一段随机时间,模拟任务执行
Thread.sleep((long) (Math.random() * 1000));
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(getName() + " 执行完毕!");
}
}
public static void main(String[] args)
{
System.out.println("程序开始执行...");
// 创建两个线程对象
MyThread thread1 = new MyThread("你好,世界");
MyThread thread2 = new MyThread("Hello, World");
// 设置线程名称
thread1.setName("线程1");
thread2.setName("线程2");
// 启动线程
thread1.start(); // 注意:不要直接调用run()方法
thread2.start();
// 主线程继续执行
for (int i = 0; i < 3; i++) {
System.out.println("主线程执行 - 第" + (i + 1) + "次");
try {
Thread.sleep(500);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("主线程执行完毕,但程序不会立即结束,因为还有其他线程在运行");
}
}
重要: 启动线程必须调用start()方法,而不是直接调用run()方法。调用start()方法会创建一个新线程并使这个线程开始执行run()方法;而直接调用run()方法只会在当前线程中执行该方法,不会创建新线程。
3.2 实现Runnable接口
通过实现Runnable接口并实现其run()方法来创建一个任务,然后将该任务传递给Thread对象:
package org.devlive.tutorial.multithreading.chapter01;
/**
* 通过实现Runnable接口实现多线程
*/
public class RunnableImplDemo
{
// 自定义任务类,实现Runnable接口
static class MyRunnable
implements Runnable
{
private final String message;
public MyRunnable(String message)
{
this.message = message;
}
// 实现run方法,定义任务执行的内容
@Override
public void run()
{
// 获取当前执行的线程
Thread currentThread = Thread.currentThread();
// 打印信息,显示当前线程名称
for (int i = 0; i < 5; i++) {
System.out.println(currentThread.getName() + " 执行: " + message + " - 第" + (i + 1) + "次");
try {
// 线程休眠一段随机时间,模拟任务执行
Thread.sleep((long) (Math.random() * 1000));
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(currentThread.getName() + " 执行完毕!");
}
}
public static void main(String[] args)
{
System.out.println("程序开始执行...");
// 创建两个Runnable对象
Runnable task1 = new MyRunnable("你好,世界");
Runnable task2 = new MyRunnable("Hello, World");
// 创建线程对象,并传入Runnable任务
Thread thread1 = new Thread(task1, "线程1");
Thread thread2 = new Thread(task2, "线程2");
// 启动线程
thread1.start();
thread2.start();
// 主线程继续执行
for (int i = 0; i < 3; i++) {
System.out.println("主线程执行 - 第" + (i + 1) + "次");
try {
Thread.sleep(500);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("主线程执行完毕,但程序不会立即结束,因为还有其他线程在运行");
}
}
3.3 两种方式的比较
特点 | 继承Thread类 | 实现Runnable接口 |
代码结构 | 需要继承Thread类,Java不支持多继承,限制了类的扩展性 | 只需实现Runnable接口,可以继承其他类,更加灵活 |
资源共享 | 每个线程都是独立的对象,不方便在多个线程间共享数据 | 可以多个线程使用同一个Runnable对象,便于共享数据 |
耦合性 | 任务和线程高度耦合 | 任务和线程分离,解耦合 |
适用场景 | 简单的独立线程任务 | 需要共享数据或复用任务的场景 |
提示: 在实际开发中,通常推荐使用实现Runnable接口的方式,因为它更加灵活,也符合设计原则中的"组合优于继承"原则。
3.4 使用Java 8 Lambda表达式简化Runnable实现
从Java 8开始,我们可以使用Lambda表达式大大简化Runnable的实现:
package org.devlive.tutorial.multithreading.chapter01;
/**
* 使用Lambda表达式简化多线程创建
*/
public class LambdaThreadDemo
{
public static void main(String[] args)
{
System.out.println("程序开始执行...");
// 使用Lambda表达式创建Runnable实例
Runnable task1 = () -> {
Thread currentThread = Thread.currentThread();
for (int i = 0; i < 5; i++) {
System.out.println(currentThread.getName() + " 执行: 你好,世界 - 第" + (i + 1) + "次");
try {
Thread.sleep((long) (Math.random() * 1000));
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(currentThread.getName() + " 执行完毕!");
};
// 再简化一点,直接在创建线程时使用Lambda表达式
Thread thread1 = new Thread(task1, "线程1");
Thread thread2 = new Thread(() -> {
Thread currentThread = Thread.currentThread();
for (int i = 0; i < 5; i++) {
System.out.println(currentThread.getName() + " 执行: Hello, World - 第" + (i + 1) + "次");
try {
Thread.sleep((long) (Math.random() * 1000));
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(currentThread.getName() + " 执行完毕!");
}, "线程2");
// 启动线程
thread1.start();
thread2.start();
// 主线程继续执行
for (int i = 0; i < 3; i++) {
System.out.println("主线程执行 - 第" + (i + 1) + "次");
try {
Thread.sleep(500);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("主线程执行完毕,但程序不会立即结束,因为还有其他线程在运行");
}
}
Lambda表达式使代码更加简洁,特别适合简单的Runnable实现。
4. 实战案例:创建并启动你的第一个线程
现在,让我们通过一个实战案例来综合运用所学知识。我们将创建一个模拟文件下载的程序,使用多线程同时下载多个文件。
package org.devlive.tutorial.multithreading.chapter01;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* 多线程文件下载模拟器
*/
public class FileDownloaderDemo
{
// 文件下载器,实现Runnable接口
static class FileDownloader
implements Runnable
{
private final String fileName;
private final int fileSize; // 模拟文件大小,单位MB
public FileDownloader(String fileName, int fileSize)
{
this.fileName = fileName;
this.fileSize = fileSize;
}
@Override
public void run()
{
System.out.println(getCurrentTime() + " - 开始下载文件:" + fileName + "(" + fileSize + "MB)");
// 模拟下载过程
try {
for (int i = 1; i <= 10; i++) {
// 计算当前下载进度
int progress = i * 10;
int downloadedSize = fileSize * progress / 100;
// 休眠一段时间,模拟下载耗时
TimeUnit.MILLISECONDS.sleep(fileSize * 50);
// 打印下载进度
System.out.println(getCurrentTime() + " - " + Thread.currentThread().getName()
+ " 下载 " + fileName + " 进度: " + progress + "% ("
+ downloadedSize + "MB/" + fileSize + "MB)");
}
System.out.println(getCurrentTime() + " - " + Thread.currentThread().getName()
+ " 下载完成:" + fileName);
}
catch (InterruptedException e) {
System.out.println(getCurrentTime() + " - " + Thread.currentThread().getName()
+ " 下载中断:" + fileName);
Thread.currentThread().interrupt(); // 重设中断状态
}
}
// 获取当前时间的格式化字符串
private String getCurrentTime()
{
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
return sdf.format(new Date());
}
}
public static void main(String[] args)
{
System.out.println("=== 文件下载模拟器 ===");
// 创建多个下载任务
FileDownloader task1 = new FileDownloader("电影.mp4", 200);
FileDownloader task2 = new FileDownloader("音乐.mp3", 50);
FileDownloader task3 = new FileDownloader("文档.pdf", 10);
// 创建线程执行下载任务
Thread thread1 = new Thread(task1, "下载线程-1");
Thread thread2 = new Thread(task2, "下载线程-2");
Thread thread3 = new Thread(task3, "下载线程-3");
// 启动线程,开始下载
thread1.start();
thread2.start();
thread3.start();
// 主线程监控下载进度
try {
// 等待所有下载线程完成
thread1.join();
thread2.join();
thread3.join();
System.out.println("\n所有文件下载完成!");
}
catch (InterruptedException e) {
System.out.println("主线程被中断");
}
}
}
在这个实例中,我们模拟了三个不同大小文件的并行下载过程。通过使用多线程,这三个文件可以同时下载,而不需要等待一个文件下载完成后再开始下载下一个文件。join()方法使主线程等待所有下载线程完成后才结束程序。
常见问题与解决方案
问题1:Thread.sleep()方法抛出InterruptedException
问题描述: 为什么使用Thread.sleep()方法必须捕获InterruptedException异常?
解决方案: sleep()方法可能会被其他线程中断,此时会抛出InterruptedException。正确的处理方式是捕获异常并重设中断状态:
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// 记录日志或者执行必要的清理工作
Thread.currentThread().interrupt(); // 重设中断状态
}
问题2:直接调用run()方法而不是start()方法
问题描述: 为什么直接调用run()方法不会创建新线程?
解决方案: 直接调用run()方法只是在当前线程中执行该方法,不会启动新线程。必须调用start()方法才能创建新线程并执行run()方法。
问题3:多线程执行顺序不确定
问题描述: 如何确保多个线程按特定顺序执行?
解决方案: 可以使用join()方法让一个线程等待另一个线程完成:
Thread thread1 = new Thread(() -> {
// 线程1的任务
});
Thread thread2 = new Thread(() -> {
try {
thread1.join(); // 等待thread1完成
// 线程2的任务
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
thread1.start();
thread2.start();
小结
在这一章中,我们学习了以下核心内容:
- 线程概念: 了解了什么是线程,以及线程作为程序执行的最小单位的概念。
- 多线程优势: 掌握了为什么要使用多线程编程,包括提高CPU利用率、改善程序响应性、更好的资源利用以及简化复杂问题处理。
- 线程创建方式: 学习了Java中创建线程的两种基本方式:继承Thread类和实现Runnable接口,以及它们各自的优缺点。
- 简化线程创建: 了解了如何使用Java 8 Lambda表达式简化Runnable的实现。
- 实战应用: 通过一个文件下载模拟器的实例,综合应用了所学的多线程知识。
通过本章的学习,你已经具备了创建和启动Java线程的基本能力。在后续章节中,我们将深入探讨线程的生命周期、线程同步和安全等更高级的多线程编程主题。
记住一点:多线程编程是Java开发中的重要技能,但也是比较复杂的主题。掌握好基础概念和实践案例,是走向高级多线程编程的关键第一步。
在下一章,我们将详细介绍线程的生命周期和状态转换,帮助你更深入理解线程的工作机制。
本章节源代码地址为
https://github.com/qianmoQ/tutorial/tree/main/java-multithreading-tutorial/src/main/java/org/devlive/tutorial/multithreading/chapter01
相关推荐
- 无力吐槽的自动续费(你被自动续费困扰过吗?)
-
今天因为工作需要,需要在百度文库上下载一篇文章。没办法,确实需要也有必要,只能老老实实的按要求买了个VIP。过去在百度文库上有过类似经历,当时为了写论文买了一个月的VIP,后面也没有太注意,直到第二个...
- 百度文库推出“文源计划”创作者可一键认领文档
-
11月7日,百度文库发布了旨在保护创作者权益的“文源计划”。所谓“文源计划”,即为每一篇文档找到源头,让创作者享受更多的权益。据百度文库总经理李小婉介绍,文源计划分为三部分,分别是版权认证、版权扶持和...
- 有开放大学学号的同学,百度文库高校版可以用了。
-
还在网上找百度文库的下载方式,只要从身边的朋友在读开放大学的,那他(她)的学号就可以登陆到国家开放大学图书馆,还使用百度文库高校版来下载。与百度文库稍有不同,但足够使用了。现转国图链接如下:htt...
- 搜索资源方法推荐(搜索资源的方法)
-
今天msgbox就要教大家如何又快又准的搜到各类资源,第一点,排除干扰百度搜索出来啊经常前排展示它的产品以及百度文库,如何去除呢?很简单,后面输入空格减号百度文库,比如你搜高等数学百度文库很多,只要后...
- 一行代码搞定百度文库VIP功能(2021百度文库vip账号密码共享)
-
百度文库作为大家常用查资料找文档的平台,大多数文档我们都可以直接在百度文库找到,然而百度文库也有让人头痛的时候。好不容易找到一篇合适的文档,当你准备复制的时候他却提示你需要开通VIP才能复制~~~下载...
- 百度文库文档批量上传工具用户说明书
-
百度文库文档批量上传工具用户说明书1、软件主要功能1、批量上传文档到百度文库,支持上传到收费、VIP专享、优享以及共享。2、支持自动分类和自动获取标签3、支持多用户切换,一个账户传满可以切换到...
- 百度文库现在都看不到文档是否上传成功,要凉了吗?
-
打开知识店铺,百度文库文档里显示都是下载这一按键,上传的文档也看不到是否成功?咋情况,要取消了吗?没通过审核的也不让你删除,是几个意思,想通吃吗?现在百度上传文档也很费劲,有时弄了半天的资料上传审核过...
- 微信推广引流108式:利用百度文库长期分享软文引流
-
百度文库相对于百度知道、百度百科来说,操作上没那么多条条框框,规则上也相对好把握些。做一条百度知道所花费的精力一般都会比做一条百度文库的要多些,老马个人操作下来觉得百度文库更好把握。但见仁见智吧,今天...
- 职场“避雷”指南 百度文库推出标准化劳动合同范本
-
轰轰烈烈的毕业季结束了,众多应届生在经过了“职场海选”后,已正式成为职场生力军的一员。这一阶段,除了熟悉业务,签订劳动合同、了解职场福利也迅速被提上日程。而随着国人法律意识的增强,百度文库内《劳动合同...
- 《百度文库》:素材精选宝库(百度文库官网首页)
-
《百度文库》:独特功能助力选择高质量素材在当今信息爆炸的时代,如何高效地获取并利用有价值的素材成为了许多人面临的挑战。而《百度文库》作为百度公司推出的一款在线文档分享平台,凭借其丰富的资源、强大的功能...
- 深度整合和开放AI能力 百度文库和网盘推出内容操作系统「沧舟OS」
-
【TechWeb】4月25日消息,Create2025百度AI开发者大会上,百度文库和百度网盘推出全球首个内容操作系统——沧舟OS。基于沧舟OS,百度文库APP全新上线「GenFlow超能搭子」...
- 女子发现大二作业被百度文库要求付费下载,律师:平台侵权,应赔偿
-
近日,28岁的黎女士在百度百科搜索家乡的小地名时,发现了自己在大二完成的课题作业。她继续搜索,发现多个平台收录了该文,比如豆丁网和文档之家等,有的还设置了付费或积分下载。2月15日,九派新闻记者以用户...
- 2016杀入百度文库的新捷径,只有少数人才知道的喔
-
百度的产品在SEO优化中的分量真不用多说,其实很多人都像我一样一直在找捷径。但是我经常发现很多人都是在用死方法。比如发贴吧发帖而不知道去申请一个吧主,知道自问自答而不知道去申请一个合作资格。口碑和贴吧...
- 百度文库付费文档搜索方法(百度文库付费文档搜索方法有哪些)
-
一直以来,百度文库中无论是个人中心还是个人主页,都没有像淘宝一样的店内搜索功能,连最近新开的知识店铺也没有设计店内搜索功能,这无论是对上传用户还是下载用户都不方便,上传用户想要搜索自己的文档无法办到...
- 供读者免费使用!泰达图书馆机构版百度文库新年上新啦
-
在泰达图书馆读者使用百度文库数字资源不需要VIP,免-费-用!惊不惊喜?快来了解一下吧……新年伊始,为满足区域企业、高校、科研院所以及居民群众在教学、科研及学习过程中,对各类文献资源的需求,泰达图书馆...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 如何绘制折线图 (52)
- javaabstract (48)
- 新浪微博头像 (53)
- grub4dos (66)
- s扫描器 (51)
- httpfile dll (48)
- ps实例教程 (55)
- taskmgr (51)
- s spline (61)
- vnc远程控制 (47)
- 数据丢失 (47)
- wbem (57)
- flac文件 (72)
- 网页制作基础教程 (53)
- 镜像文件刻录 (61)
- ug5 0软件免费下载 (78)
- debian下载 (53)
- ubuntu10 04 (60)
- web qq登录 (59)
- 笔记本变成无线路由 (52)
- flash player 11 4 (50)
- 右键菜单清理 (78)
- cuteftp 注册码 (57)
- ospf协议 (53)
- ms17 010 下载 (60)