深入简出的带你精通java线程

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 线程与进程是操作系统中的两个重要概念。进程是资源分配的最小单位,负责加载指令、管理内存和IO;线程是CPU调度的最小单位,也被称为轻量级进程。

线程与进程

  • 进程:用来加载指令、管理内存、管理IO。操作系统会以进程为单位,分配系统资源(CPU时间片、内存等资源),进程是资源分配的最小单位。
  • 线程:有时被称为轻量级进程(Lightweight Process,LWP),是操作系统调度(CPU调度)执行的最小单位。

进程间通信的方式

  • 管道(pipe)及有名管道(named pipe) :管道可用于具有亲缘关系的父子进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。
  • 信号(signal) :信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一致的。
  • 消息队列(message queue) :消息队列是消息的链接表,它克服了上两种通信方式中信号量有限的缺点,具有写权限的进程可以按照一定的规则向消息队列中添加新信息;对消息队列有读权限的进程则可以从消息队列中读取信息。
  • 共享内存(shared memory) :可以说这是最有用的进程间通信方式。它使的多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。
  • 信号量(semaphore) :主要作为进程之间及同一种进程的不同线程之间的同步和互斥手段。
  • 套接字(socket) :这是一种更为一般的进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛。

线程的同步互斥

  • 线程同步:线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。
  • 线程互斥:对于共享的进程系统资源,在各单个线程访问时的排它性。
线程同步互斥的控制方法
  • 临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。(在一段时间内只允许一个线程访问的资源就称为临界资源)。
  • 互斥量:为协调共同对一个共享资源的单独访问而设计的。
  • 信号量:为控制一个具有有限数量用户资源而设计。
  • 事件:用来通知线程有一些事件已发生,从而启动后继任务的开始。

线程上下文切换(Context switch):一般要5-10毫秒

  • 下文切换是指CPU(中央处理单元)从一个进程或线程到另一个进程或线程的切换。
  • 是多任务操作系统的一个基本特性。
  • 切换的耗时一般为5-10毫秒。
  • 上下文切换只能在内核模式下发生!

操作系统层面线程状态:初始状态、可运行状态、运行状态、休眠状态和终止状态。

  • 初始状态:指的是线程已经被创建,但是还不允许分配 CPU 执行。
  • 可运行(就绪)状态:指的是线程可以分配 CPU 执行。在这种状态下,真正的操作系统线程已经被成功创建了,所以可以分配 CPU 执行。
  • 运行状态:获取到CPU的时间片。
  • 休眠状态:运行状态的线程如果调用一个阻塞的 API(例如以阻塞方式读文件)或者等待某个事件(例如条件变量),那么线程的状态就会转换到休眠状态,同时释放 CPU 使用权,休眠状态的线程永远没有机会获得 CPU 使用权。
  • 终止状态:线程执行完或者出现异常就会进入终止状态,终止状态的线程不会切换到其他任何状态,进入终止状态也就意味着线程的生命周期结束了。

java的线程状态

  • 取自Thread类的内部枚举

java

代码解读

复制代码

    public enum State {
        // 初始化状态
        NEW,

        // 可运行状态+运行状态
        RUNNABLE,

        // 阻塞状态
        BLOCKED,

        // 无时限等待
        WAITING,

        // 有时限等待
        TIMED_WAITING,

        // 终止状态
        TERMINATED;
    }

面试中问你线程的状态,你应该如何回答?

  • 在操作系统层面有5种,java中有6种。
  • Java线程中的 BLOCKED、WAITING、TIMED_WAITING 是一种状态,即操作系统的休眠状态。这三种状态永远没有CPU的使用权!
  • Java线程中的 RUNNABLE 状态,在操作系统中分为:可运行(就绪)状态、运行状态。

Java线程的实现方式(4种)

  • 使用 Thread类或继承Thread类

java

代码解读

复制代码

// 创建线程对象
Thread t = new Thread() {
    public void run() {
    // 要执行的任务
    }
};
// 启动线程
  • 实现 Runnable 接口配合Thread:线程(Thread)与任务(Runnable)分开

java

代码解读

复制代码

Runnable runnable = new Runnable() {
    public void run(){
    // 要执行的任务
    }
};
// 创建线程对象
Thread t = new Thread( runnable );
// 启动线程
  • 使用有返回值的 Callable

java

代码解读

复制代码

class CallableTask implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        return new Random().nextInt();
    }
}
//创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//提交任务,并用 Future提交返回结果
  • 使用 lambda

java

代码解读

复制代码

new Thread(() -> System.out.println(Thread.currentThread().getName())).start();

Thread常用方法

sleep方法
  • 调用 sleep 会让当前线程从 Running 进入TIMED_WAITING状态,不会释放对象锁
  • 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException,并且会清除中断标志
  • 睡眠结束后的线程未必会立刻得到执行
  • sleep当传入参数为0时,和yield相同
yield方法
  • yield会释放CPU资源,让当前线程从 Running 进入 Runnable状态,让优先级更高(至少是相同)的线程获得执行机会,不会释放对象锁;
  • 假设当前进程只有main线程,当调用yield之后,main线程会继续运行,因为没有比它优先级更高的线程;
  • 具体的实现依赖于操作系统的任务调度器
join方法
  • 等待调用join方法的线程结束之后,程序再继续执行,一般用于等待异步线程执行完结果之后才能继续运行的场景。

如何正确优雅的停止线程?

stop方法
  • stop()方法已经被jdk废弃,原因就是stop()方法太过于暴力,强行把执行到一半的线程终止。
Java线程的中断机制
  • 中断机制是一种协作机制,也就是说通过中断并不能直接终止另一个线程,而需要被中断的线程自己处理。
  • 被中断的线程拥有完全的自主权,它既可以选择立即停止,也可以选择一段时间后停止,也可以选择压根不停止。
  • interrupt(): 将线程的中断标志位设置为true,不会停止线程
  • isInterrupted(): 判断当前线程的中断标志位是否为true,不会清除中断标志位
  • Thread.interrupted():判断当前线程的中断标志位是否为true,并清除中断标志位,重置为fasle
  • sleep可以被中断 抛出中断异常:sleep interrupted, 清除中断标志位
  • wait可以被中断 抛出中断异常:InterruptedException, 清除中断标志位

Java线程间通信方式

  • volatile:两大特性,一是可见性,二是有序性,禁止指令重排序,其中可见性就是可以让线程之间进行通信。
  • 等待唤醒(等待通知)机制:基于wait和notify方法来实现,在一个线程内调用该线程锁对象的wait方法,线程将进入等待队列进行等待直到被唤醒。
  • LockSupport:JDK中用来实现线程阻塞和唤醒的工具,线程调用park则等待“许可”,调用unpark则为指定线程提供“许可”。
  • 管道输入输出流:PipedOutputStream、PipedInputStream、PipedReader和PipedWriter,前两种面向字节,而后两种面向字符。
  • Thread.join:可以简单理解为线程合并。一个线程等待到另一个线程执行完。

为什么说本质上Java中实现线程只有一种方式?

java

代码解读

复制代码


    private Runnable target;

    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
  • 继承Thread类,其实就是使用的Runnable对象调用run方法

java

代码解读

复制代码

public interface ThreadFactory {

    Thread newThread(Runnable r);
}
  • 线程池的创建,本质上也是传入一个Runnable对象
  • 使用lambda的方式,本质与继承的方式一致
  • 所以,创建线程的本质只有一种!

简单了解start()方法是如何开启一个线程的(JVM与操作系统层面)?

  • 使用new Thread()创建一个线程,然后调用start()方法进行java层面的线程启动;
  • 调用本地方法start0(),去调用jvm中的JVM_StartThread方法进行线程创建和启动;
  • 调用new JavaThread(&thread_entry, sz)进行线程的创建,并根据不同的操作系统平台调用对应的os::create_thread方法进行线程创建;
  • 新创建的线程状态为Initialized,调用了sync->wait()的方法进行等待,等到被唤醒才继续执行thread->run();
  • 调用Thread::start(native_thread);方法进行线程启动,此时将线程状态设置为RUNNABLE,接着调用os::start_thread(thread),根据不同的操作系统选择不同的线程启动方式;
  • 线程启动之后状态设置为RUNNABLE, 并唤醒第4步中等待的线程,接着执行thread->run()的方法;
  • JavaThread::run()方法会回调第1步new Thread中复写的run()方法。


转载来源:https://juejin.cn/post/7067440659828850725

相关文章
|
7天前
|
机器学习/深度学习 人工智能 自然语言处理
PAI Model Gallery 支持云上一键部署 DeepSeek-V3、DeepSeek-R1 系列模型
DeepSeek 系列模型以其卓越性能在全球范围内备受瞩目,多次评测中表现优异,性能接近甚至超越国际顶尖闭源模型(如OpenAI的GPT-4、Claude-3.5-Sonnet等)。企业用户和开发者可使用 PAI 平台一键部署 DeepSeek 系列模型,实现 DeepSeek 系列模型与现有业务的高效融合。
|
7天前
|
人工智能 搜索推荐 Docker
手把手教你使用 Ollama 和 LobeChat 快速本地部署 DeepSeek R1 模型,创建个性化 AI 助手
DeepSeek R1 + LobeChat + Ollama:快速本地部署模型,创建个性化 AI 助手
2686 112
手把手教你使用 Ollama 和 LobeChat 快速本地部署 DeepSeek R1 模型,创建个性化 AI 助手
|
2天前
|
云安全 边缘计算 人工智能
对话|ESA如何助力企业高效安全开展在线业务?
ESA如何助力企业安全开展在线业务
1015 7
|
5天前
|
人工智能 自然语言处理 JavaScript
宜搭上新,DeepSeek 插件来了!
钉钉宜搭近日上线了DeepSeek插件,无需编写复杂代码,普通用户也能轻松调用强大的AI大模型能力。安装后,平台新增「AI生成」组件,支持创意内容生成、JS代码编译、工作汇报等场景,大幅提升工作效率。快来体验这一高效智能的办公方式吧!
1345 5
|
14天前
|
Linux iOS开发 MacOS
deepseek部署的详细步骤和方法,基于Ollama获取顶级推理能力!
DeepSeek基于Ollama部署教程,助你免费获取顶级推理能力。首先访问ollama.com下载并安装适用于macOS、Linux或Windows的Ollama版本。运行Ollama后,在官网搜索“deepseek”,选择适合你电脑配置的模型大小(如1.5b、7b等)。通过终端命令(如ollama run deepseek-r1:1.5b)启动模型,等待下载完成即可开始使用。退出模型时输入/bye。详细步骤如下图所示,轻松打造你的最强大脑。
9441 86
|
2天前
|
人工智能 自然语言处理 API
DeepSeek全尺寸模型上线阿里云百炼!
阿里云百炼平台近日上线了DeepSeek-V3、DeepSeek-R1及其蒸馏版本等六款全尺寸AI模型,参数量达671B,提供高达100万免费tokens。这些模型在数学、代码、自然语言推理等任务上表现出色,支持灵活调用和经济高效的解决方案,助力开发者和企业加速创新与数字化转型。示例代码展示了如何通过API使用DeepSeek-R1模型进行推理,用户可轻松获取思考过程和最终答案。
|
6天前
|
API 开发工具 Python
阿里云PAI部署DeepSeek及调用
本文介绍如何在阿里云PAI EAS上部署DeepSeek模型,涵盖7B模型的部署、SDK和API调用。7B模型只需一张A10显卡,部署时间约10分钟。文章详细展示了模型信息查看、在线调试及通过OpenAI SDK和Python Requests进行调用的步骤,并附有测试结果和参考文档链接。
1393 9
阿里云PAI部署DeepSeek及调用
|
1月前
|
供应链 监控 安全
对话|企业如何构建更完善的容器供应链安全防护体系
阿里云与企业共筑容器供应链安全
171378 18
|
1月前
|
供应链 监控 安全
对话|企业如何构建更完善的容器供应链安全防护体系
随着云计算和DevOps的兴起,容器技术和自动化在软件开发中扮演着愈发重要的角色,但也带来了新的安全挑战。阿里云针对这些挑战,组织了一场关于云上安全的深度访谈,邀请了内部专家穆寰、匡大虎和黄竹刚,深入探讨了容器安全与软件供应链安全的关系,分析了当前的安全隐患及应对策略,并介绍了阿里云提供的安全解决方案,包括容器镜像服务ACR、容器服务ACK、网格服务ASM等,旨在帮助企业构建涵盖整个软件开发生命周期的安全防护体系。通过加强基础设施安全性、技术创新以及倡导协同安全理念,阿里云致力于与客户共同建设更加安全可靠的软件供应链环境。
150313 32
|
6天前
|
缓存 自然语言处理 安全
快速调用 Deepseek API!【超详细教程】
Deepseek 强大的功能,在本教程中,将指导您如何获取 DeepSeek API 密钥,并演示如何使用该密钥调用 DeepSeek API 以进行调试。