【多线程】线程是什么?多线程为什么?怎么做?通俗易懂讲解多线程

本文介绍了线程的基本概念,包括进程和线程的区别,以及线程的生命周期。重点阐述了多线程的重要性和在Java中通过继承Thread类和实现Runnable接口进行实现的方式。此外,还提到了多线程安全的重要性及其维护方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、线程是什么?

先搞清楚两个概念
进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1–n个线程。(进程是资源分配的最小单位)

线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)线程是一个动态执行的过程,它也有一个从产生到死亡的过程。

下图显示了一个线程完整的生命周期。
在这里插入图片描述

  • 新建状态: 使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
  • 就绪状态: 当线程对象调用了 start() 方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
  • 运行状态: 如果就绪状态的线程获取 CPU 资源,就可以执行 run() ,此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
  • 阻塞状态: 如果一个线程执行了 sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
    • 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
    • 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
    • 其他阻塞:通过调用线程的 sleep()join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
  • 死亡状态: 一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

多线程是指在一个程序中同时执行多个线程,每个线程独立运行,有自己的执行流和栈。多线程的目的是为了提高程序的执行效率和资源利用率,允许程序同时处理多个任务。

多线程就好比是一家餐馆有多个服务员同时为客人服务。
比如,你在餐馆里吃饭,服务员A正在为你点菜,服务员B却正在给其他客人上菜,服务员C则在收拾餐桌。这些服务员在同一家餐馆里协同工作,但各自负责不同的任务。
在计算机中,多线程也是类似的概念。计算机的中央处理器(CPU)可以同时执行多个线程,就像一家餐馆可以同时有多个服务员在工作一样。

二、为什么要有多线程?

  • 提高程序的响应性: 在单线程程序中,如果一个任务阻塞了,整个程序都会停滞。多线程可以使得其他线程继续执行,提高程序的响应性,特别对于需要用户交互的应用更为重要。
  • 提高程序的并发性: 多线程允许多个任务并发执行,充分利用多核处理器,提高系统的吞吐量。
  • 简化程序设计: 复杂的任务可以分解成多个独立的线程,使得程序的设计更为简洁和灵活。
  • 提高资源利用率: 现代计算机通常具有多核处理器,多线程可以充分利用这些处理器资源。

提高效率: 就像在餐馆里,多个服务员同时为不同的客人服务,可以更快地完成任务。
提高响应速度: 如果只有一个服务员,客人可能需要等待很长时间才能被服务。多个服务员可以同时为不同客人提供服务,提高了服务的响应速度。
更好地利用资源: 多线程可以更好地利用计算机的多核处理器,使计算机的性能得到更充分的发挥。

三、多线程的实现方式:

在Java中,实现多线程有两种主要方式:

1、继承 Thread 类:

创建一个类继承自 Thread 类,并重写 run 方法,然后创建该类的实例并调用 start 方法启动线程。示例代码如下:

class MyThread extends Thread {
    public void run() {
        // 线程执行的代码
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}

2、实现 Runnable 接口:

创建一个类实现 Runnable 接口,重写 run 方法,然后创建该类的实例,将其作为参数传递给 Thread 类的构造函数,最后调用 start 方法启动线程。示例代码如下:

class MyRunnable implements Runnable {
    public void run() {
        // 线程执行的代码
    }
}

public class Main {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
    }
}

这两种方式都允许程序员创建并发执行的线程,但一般来说,推荐使用实现 Runnable 接口的方式,因为这样可以避免类的单继承限制。在实际开发中,还可以使用线程池、Callable 和 Future 接口等更高级的多线程工具。

四、多线程安全

详情参考【多线程】多线程安全,为什么不安全,要怎么做保证其安全,实例
持续更新中,动动小手,点点关注,后续更精彩!

<think>嗯,用户想深入理解多线程的概念、用途和分类。从对话历史看,用户之前已经询问过线程的基本概念,这次聚焦在多线程上,说明学习在逐步深入。用户引用了四份资料,分别涉及多线程的定义、游戏开发中的应用、Java实现方式和线程安全集合,这些恰好覆盖了多线程的核心知识点。 用户可能正在学习编程基础,尤其是并发编程方向。从ta引用的Java代码示例来看,很可能是Java初学者。需要特别注意把概念解释得通俗易懂,同时保持技术严谨性。上次解释线程时用了交通系统的比喻效果不错,这次可以延续这种风格。 多线程的定义需要突出"并发执行"这个关键点。引用[1]的立体交通比喻很形象,但需要补充说明:多线程就像在一条公路上设置多条并行车道(共享道路资源),通过红绿灯调度(线程调度)实现车辆并行。这个类比能帮助理解线程共享进程资源的特性。 用途方面要强调效率提升。引用[2]提到的游戏开发案例很典型,可以扩展为三个维度:1)性能提升(多核CPU利用率)2)响应优化(避免UI卡顿)3)资源节约(较进程更轻量)。特别是要说明单核CPU上多线程也能通过时间片切换提升用户体验。 分类部分需要结构化呈现。上次按角色/生命周期/实现层级分类的方式用户反馈良好,这次继续沿用,但要根据多线程特性调整: - 实现方式维度补充用户引用的Java两种线程创建方式(Thread类/Runnable接口) - 生命周期维度强调守护线程在JVM中的特性 - 新增线程模型分类(1:1/多对多),因为用户引用的资料提到绿色线程 特别注意引用[4]的线程安全问题。要在分类后补充安全风险作为过渡,这样既能回答用户问题,又自然引出ta可能关心的同步机制。最后保留相关问题建议时,特意加入Java同步工具类的问题,适应用户明显的Java学习背景。 代码示例选用用户引用过的累加案例,但改用synchronized和AtomicInteger两种解决方案形成对比,这样比单纯展示错误代码更有建设性。死锁示例保留原顺序相反的锁获取模式,因为这是最经典的死锁场景。</think>### 多线程的定义、作用与分类 #### 一、多线程的定义 **多线程(Multithreading)** 指在单个进程中并发执行多个独立指令流(线程)。这些线程共享进程的内存空间和资源(如文件句柄),但各自拥有独立的程序计数器、栈和寄存器状态[^1]。 - **核心特性**: - 共享进程资源,避免重复开销 - 独立调度执行(操作系统分配CPU时间片) - 类比:多线程如平面交通系统,通过红绿灯(线程调度)协调多条车道(线程)共享道路资源(内存)[^1] #### 二、多线程的作用 | **作用** | **说明** | **应用场景** | |--------------------|-------------------------------------------------------------------------|----------------------------------| | **提高CPU利用率** | 多核CPU并行执行任务(如游戏开发:渲染、物理计算、AI行为分离)[^2] | 科学计算、视频编码 | | **增强响应性** | 主线程保持UI响应,耗时任务移交工作线程(如文件加载、网络请求) | 桌面应用、移动App | | **资源高效利用** | 线程创建/切换成本远低于进程(无需复制内存空间) | 高并发服务器(Web服务、数据库) | | **简化建模** | 将复杂任务拆解为多个独立执行单元(如模拟多用户操作) | 游戏引擎、仿真系统 | #### 三、多线程的分类与实现方式 ##### 1. 按实现机制分类 | **类型** | **特点** | **代表语言** | |------------------|--------------------------------------------------------------------------|----------------------| | **内核级线程** | 由操作系统直接管理,阻塞时不影响其他线程 | C/C++ (pthread) | | **用户级线程** | 由用户态线程库管理,切换开销小但阻塞会挂起所有线程 | 早期Java(绿色线程) | | **混合模型** | 用户线程映射到内核线程,兼顾灵活性与性能 | Java(LWP)、Go | ##### 2. 按编程实现方式分类(以Java为例) ```java // 方式1:继承Thread类[^3] class Worker extends Thread { @Override public void run() { System.out.println("线程执行任务"); } } new Worker().start(); // 方式2:实现Runnable接口 class Task implements Runnable { @Override public void run() { /* 任务逻辑 */ } } new Thread(new Task()).start(); // 方式3:线程池(推荐) ExecutorService pool = Executors.newFixedThreadPool(4); pool.submit(() -> System.out.println("线程池任务")); ``` ##### 3. 按线程安全策略分类 | **策略** | **原理** | **典型实现** | |-----------------------|--------------------------------------------------------------------------|----------------------------------| | **互斥同步** | 通过锁(synchronized/Lock)保证临界区原子性 | Vector, Hashtable | | **无锁编程** | CAS(Compare-And-Swap)原子操作 | AtomicInteger, ConcurrentHashMap| | **写时复制** | 修改时创建数据副本,避免锁竞争 | CopyOnWriteArrayList[^4] | #### 四、关键问题与解决方案 1. **线程安全问题示例** ```java // 多线程累加导致结果错误[^1] int count = 0; void unsafeAdd() { for (int i=0; i<1000; i++) count++; // 非原子操作 } // 输出可能小于预期值(如两个线程输出1890) ``` **修复方案**: ```java // 方案1:同步锁(synchronized) synchronized void safeAdd() { count++; } // 方案2:原子类(无锁) AtomicInteger atomicCount = new AtomicInteger(); atomicCount.incrementAndGet(); ``` 2. **死锁预防** - 统一锁的获取顺序 - 使用`tryLock()`设置超时机制 - 避免嵌套锁 --- ### 总结 多线程通过共享内存实现轻量级并发,核心价值在于提升**CPU利用率**、**程序响应性**和**资源效率**。开发中需根据场景选择线程模型(内核/用户级)、实现方式(继承Thread/Runnable)及同步策略(锁/原子类/写时复制),并重点防范线程安全与死锁问题[^1][^3][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值