当前位置: 首页 > news >正文

初尝并行编程

进程被分为后台进程和应用进程

大部分后台进程在系统开始运行时被操作系统启动,完成操作系统的基础服务功能。大部分应用进程由用户启动,完成用户所需的具体应用功能

进程由程序段、数据段、进程控制块三部分组成

程序段也被称为是代码段,代码段是进程的操作数据在内存中的位置,包括需要操作的数据集合;程序控制块包含进程的描述信息和控制信息,是进程的存在的唯一标识

程序控制块(Program Control Block,PCB)包含进程的描述信息和控制信息,是进程存在的唯一标 志。

PCB分为四部分

  • 进程的描述信息
    • 进程ID,进程名称,进程ID是唯一的,进程状态,进程优先级
  • 进程的调度信息
    • 程序起始地址
    • 通信信息
  • 进程的资源信息
    • 内存信息,内存占用情况和内存管理所用的数据结构
    • I/O设备信息
    • 文件句柄
  • 进程的上下文
    • 执行时各种CPU寄存器的值
    • 当前线程计数器
    • 各种栈值信息

线程由三部分组成

  • 线程的描述信息
    • 线程ID
    • 线程名称
    • 线程优先级
    • 线程状态
    • 其他,例如是否为守护线程
  • 程序计数器
  • 栈内存

通过实现Runnable接口的方式创建线程目标类

缺点:

  • 所创建的类并不是线程类,而是线程的执行目标类
  • 如果访问当前线程的属性,不能直接访问Thread的实例方法,必须通过Thread.currentThread()获取当前的线程实例,才能访问和控制当前线程。

优点:

  • 避免Java单继承的局限性,如果异步逻辑所在的类已经继承了一个基类,就没办法继承Thread类
  • 逻辑和数据更好的分离

继承Thread类实现多线程能更好地做到多个线程并发的完成各自的任务,访问各自的数据

使用Callable和FurtureTask创建线程

RunnableFurture接口

Future接口提供了三大功能:

  • 能够取消异步执行的任务
  • 判断异步任务是否执行完成
  • 获取异步任务完成后的结果

通过线程池创建执行目标提交

private static ExecutorService pool =
Executors.newFixedThreadPool(3);

线程的调度模型

  • 分时调度模型

    系统平均分配CPU时间片

  • 抢占式调度模型

    优先级高的优先分配

线程的生命周期

public static enum State {NEW, //新建RUNNABLE, //可执行:包含操作系统的就绪、运
行两种状态BLOCKED, //阻塞WAITING, //等待TIMED_WAITING, //限时等待TERMINATED; //终止}

四种常见状态

  • NEW状态

    创建成功但是没有调用start()方 法启动的Thread线程实例都处于NEW状态。

  • RUNNABLE状态

    当Java 线程的Thread实例的start()方法被调用后,操作系统中的对应线程进 入的并不是运行状态,而是就绪状态,而Java线程并没有这个就绪状 态。

    一旦就绪状态被系统选中,获得CPU时 间片,线程就开始占用CPU,开始执行线程的代码,这时线程的操作系 统状态发生了改变,进入了运行状态。

  • TERMINATED状态

    处于RUNNABLE状态的线程在run()方法执行完成之后就变成终止状 态TERMINATED了。

  • TIMED_WAITING状态

    线程处于一种特殊的等待状态,准确地说,线程处于限时等待状态。

    • Thread.sleep(int n):使得当前线程进入限时等待状态, 等待时间为n毫秒。
    • Object.wait():带时限的抢占对象的monitor锁
    • Thread.join():带时限的线程合并。
    • LockSupport.parkNanos():让线程等待,时间以纳秒为单 位。
    • LockSupport.parkUntil():让线程等待,时间可以灵活设置
  • yield仅能使一个线程从运行状态变为就绪状态,而不是阻塞状态

  • yield不能保证使得当前正在运行的线程迅速转换到就绪状 态。

  • 即使完成了迅速切换,系统通过线程调度机制从所有就绪线 程中挑选下一个执行线程时,就绪的线程有可能被选中,也有可能不 被选中,其调度的过程受到其他因素(如优先级)的影响

守护线程和用户线程

守护线程在JVM中相当于保姆的角色:只 要JVM实例中尚存在任何一个用户线程没有结束,守护线程就能执行自 己的工作;只有当最后一个用户线程结束,守护线程随着JVM一同结束 工作。

守护线程和用户线程的本质区别是:二者与JVM虚拟机进 程终止的方向不同。用户线程和JVM进程是主动关系,如果用户线程全 部终止,JVM虚拟机进程也随之终止;守护线程和JVM进程是被动关 系,如果JVM进程终止,所有的守护线程也随之终止

使用守护线程需要注意的方面:

  • 守护线程必须在启动前将其守护状态设置为true,启动之后 不能再将用户线程设置为守护线程,否则JVM会抛出一个 InterruptedException异常。
  • 守护线程存在被JVM强行终止的风险,所以在守护线程中尽 量不去访问系统资源,如文件句柄、数据库连接等。守护线程被强行 终止时,可能会引发系统资源操作不负责任的中断,从而导致资源不 可逆的损坏。
  • 守护线程创建的线程也是守护线程。

线程进入就绪状态的条件:

  • 调用线程的start()方法,此线程就会进入就绪状态。
  • 当前线程的时间片用完
  • 线程睡眠(Sleep)操作结束
  • 对其他线程合入(Join)操作结束
  • 等待用户输入结束
  • 线程争抢到对象锁(Object Monitor)
  • 当前线程调用了yield()方法让出CPU执行权限

处于阻塞状态的线程不会占用CPU的资源

进入阻塞的情况

  • 线程等待获取锁
  • IO阻塞

线程的创建

  • 必须为线程堆栈分配和初始化大量内存块,其中包含至少 1MB的栈内存
  • 需要进行系统调用,以便在OS(操作系统)中创建和注册本 地线程

线程池解决的问题:

  • 提升性能:线程池能独立负责线程的创建、维护和分配。在 执行大量异步任务时,可以不需要自己创建线程,而是将任务交给线程池去调度。线程池能尽可能使用空闲的线程去执行异步任务,最大 限度地对已经创建的线程进行复用,使得性能提升明显。
  • 线程管理:每个Java线程池会保持一些基本的线程统计信 息,例如完成的任务数量、空闲时间等,以便对线程进行有效管理, 使得能对所接收到的异步任务进行高效调度。

由于创建和销毁线程需要时间以及系统资源开销,使用线程池的好处是减少这些开销,解决资源不足的问题

JUC的线程架构

  • Executor

    Executor是Java异步目标任务的执行者接口,其目标是执行目标任务,提供了execute()接口来执行已提交的Runnable执行目标实例。

  • ExecutorService

    继承于Executor。它是Java异步目标任务的执行者服务接口,对外提供异步任务的接收服务。

  • AbstractExecutorService

    是一个抽象类,它实现了ExecutorService接口,存在的目的是为了ExecutorService中的接口默认实现

  • ThreadPoolExecutor

    线程池的实现类,继承于AbstractExecutorService抽象类

    JUC线程池的核心实现类 提供了指定数量的可重用线程,所以使用线程需要很大的开销

  • ScheduleExcutorService

    是一个接口可以完成延时和周期性任务的调度线程池的接口。其功能和Timer/TimerTask类似

  • ScheduledThreadPoolExecutor

  • Executors

    静态工厂类,通过静态工厂方法返回线程池的实例对象

    单线程化的线程池中的任务是按照提交次序顺序执行的

    池中唯一线程的存活时间是无限的

    当池中唯一线程正繁忙时,新提交的任务实例会进入内部的阻塞队列中,并且其阻塞队列是无界的

    用例最后调用shutdown()方法来关闭线程池。

newFixedThreadPool创建“固定数量的线程池”

  • 如果固定数量线程池没有达到“固定数量”,每次提交一个任务线程池就创建一个新的线程,直到线程达到线程池的固定数量。
  • 线程池的大小一旦达到“固定数量”就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新的线程
  • 在接受异步任务的执行目标实例时,如果池中所有的线程均处于繁忙状态,新任务会进入阻塞队列中(无界的阻塞队列)

固定数量线程池适用场景;需要任务长期执行的场景。固定数量的线程池的线程数量能比较稳定的保证一个数,避免频繁的回收线程和创建线程,固适用于处理CPU密集型任务,在CPU被工作线程长时间占用时,能确保尽可能少的分配线程

弊端:

  • 内部使用无界队列来存放排队任 务,当大量任务超过线程池最大容量需要处理时,队列无限增大,使 服务器资源迅速耗尽。

newCachedThreadPool创建“可缓存线程池”

任务被拒绝有两种情况:

  • 线程池已经被关闭
  • 工作队列已满且maxmumPoolSize已满

线程池的拒绝策略

  • AbortPolicy:拒绝策略(线程池默认的策略)

    使用该策略时,如果线程池队列满了,新任务就会被拒绝,并且 抛出RejectedExecutionException异常。该策略是线程池默认的拒绝 策略。

  • DiscardPolicy:抛弃策略

    该策略是AbortPolicy的Silent(安静)版本,如果线程池队列满 了,新任务就会直接被丢掉,并且不会有任何异常抛出。

  • DiscardOldestPolicy:抛弃最老任务策略

    抛弃最老任务策略,也就是说如果队列满了,就会将最早进入队 列的任务抛弃,从队列中腾出空间,再尝试加入队列。因为队列是队 尾进队头出,队头元素是最老的,所以每次都是移除队头元素后再尝 试入队。

  • CallerRunsPolicy:调用者执行策略

    调用者执行策略。在新任务被添加到线程池时,如果添加失败, 那么提交任务线程会自己去执行该任务,不会使用线程池中的线程去 执行新任务。

  • 自定义策略

    如果以上拒绝策略都不符合需求,那么可自定义一个拒绝策略, 实现RejectedExecutionHandler接口的rejectedExecution方法即可。

线程池的五种状态

  • RUNNING:线程池创建之后的初始状态,这种状态下可以执行任务
  • SHUTDOWN:该状态下线程池不再接受新任务,但是会将工作队列中的任务执行完毕
  • STOP:该状态下线程池不再接受新任务,也不会处理工作队列的剩余任务,并且将会中断所有工作线程。
  • TIDYING:该状态下所有任务都已终止或者处理完成,将会 执行terminated()钩子方法。
  • TERMINATED:执行完terminated()钩子方法之后的状态

线程池的转换规则:

  • 线程池创建之后状态为RUNNING。
  • 执行线程池的shutdown()实例方法,会使线程池状态从 RUNNING转变为SHUTDOWN。
  • 执行线程池的shutdownNow()实例方法,会使线程池状态从 RUNNING转变为STOP。
  • 当线程池处于SHUTDOWN状态时,执行其shutdownNow()方法 会将其状态转变为STOP。
  • 执行完terminated()钩子方法之后,线程池状态从TIDYING 转变为TERMINATED。

优雅的关闭线程池主要涉及的方法有三个:

  • shutdown:是JUC提供的一个有序关闭线程池的方法,此方 法会等待当前工作队列中的剩余任务全部执行完成之后,才会执行关 闭,但是此方法被调用之后线程池的状态转为SHUTDOWN,线程池不会 再接收新的任务
  • shutdownNow:是JUC提供的一个立即关闭线程池的方法,此 方法会打断正在执行的工作线程,并且会清空当前工作队列中的剩余 任务,返回的是尚未执行的任务。
  • awaitTermination:等待线程池完成关闭。在调用线程池的 shutdown()与shutdownNow()方法时,当前线程会立即返回,不会一直 等待直到线程池完成关闭。如果需要等到线程池关闭完成,可以调用 awaitTermination()方法。

使用Executors快捷创建线程池的潜在问题

  • 创建固定数量线程池的潜在问题

    主要存在于其workQueue上,其值为LinkedBlockingQueue(无 界阻塞队列)。如 果任务提交速度持续大于任务处理速度,就会造成队列中大量的任务 等待。如果队列很大,很有可能导致JVM出现OOM(Out Of Memory)异 常,即内存资源耗尽。

  • 使用Executors创建“单线程化线程池”的潜在问题

    潜在问题仍然存在于其workQueue属性上,该属性的值为 LinkedBlockingQueue(无界阻塞队列)。如果任务提交速度持续大于 任务处理速度,就会造成队列大量阻塞。如果队列很大,很有可能导 致JVM的OOM异常,甚至造成内存资源耗尽。

  • 使用Executors创建“可缓存线程池”的潜在问题

    (1)FixedThreadPool和SingleThreadPool 这两个工厂方法所创建的线程池,工作队列(任务排队的队列) 的长度都为Integer.MAX_VALUE,可能会堆积大量的任务,从而导致 OOM(即耗尽内存资源)。

    (2)CachedThreadPool和ScheduledThreadPool 这两个工厂方法所创建的线程池允许创建的线程数量为 Integer.MAX_VALUE,可能会导致创建大量的线程,从而导致OOM。

线程池的好处:

  • 降低资源消耗
  • 提高响应速度
  • 提高线程的可管理性

相关文章:

初尝并行编程

进程被分为后台进程和应用进程 大部分后台进程在系统开始运行时被操作系统启动,完成操作系统的基础服务功能。大部分应用进程由用户启动,完成用户所需的具体应用功能 进程由程序段、数据段、进程控制块三部分组成 程序段也被称为是代码段,…...

keepalived学习记录:对其vip漂移过程采用gdb跟踪

对其vip漂移过程采用gdb跟踪keepalived工具主要功能产生vip漂移过程两种情况gdb调试常用命令gdb调试时打到的函数栈(供学习参考)函数栈的图是本人理解下画的,不对请多指正 keepalived主要有三个进程,父进程是core进程,…...

51单片机串口通讯原理及程序源码-----day8

51单片机串口通讯原理及程序源码-----day8 1.定义单片机为TTL电平:高 5V 低 0V RS232电平: 计算机的串口高 -12V 低12V 所以计算机与单片机之间通讯时需要加电平转换芯片CH340T 、 MAX232。 2.通信分类: (1)并行通信通…...

mongodb入门到使用(下)

mongodb中常用命令操作一、用户操作二、创建用户三、数据库操作基本操作四、扩展操作五、集合操作一、用户操作 在mongo中使用mongodb都需要在admin数据库中操作。然后在使用下面的命令 use admin二、创建用户 db.createUser({"user":"imooc", #用户名&q…...

云HIS系统源码 医院his源码 云his源码

大型医院his系统源码 SaaS运维平台多医院入驻强大的电子病历完整文档 ,有演示 一、系统概述: 基层卫生健康云是一款满足基层医疗机构各类业务需要的健康云产品。该产品能帮助基层医疗机构完成日常各类业务,提供病患挂号支持、病患问诊、电子…...

朴素贝叶斯法学习笔记

频率派和贝叶斯派 频率派认为可以通过大量实验,从样本推断总体。比如假定总体服从均值为μ\muμ,方差为σ\sigmaσ的分布。根据中心极限定理,是可以通过抽样估算总体的参数的,而且抽样次数越多,对总体的估计就越准确。…...

vscode与C++安装与使用【不好用来骂我】

网上教程很多,但是都不太好用,这是我垃圾堆里淘金淘出来的教程: 安装软件 安装 Visual Studio Code: 你需要下载并安装 Visual Studio Code,可以在官网下载 https://code.visualstudio.com/download。 安装 C 扩展: 在 Visual S…...

C++11使用多线程(线程池)计算相似度实现性能优化

需求:图像识别中,注册的样本多了会影响计算速度,成为性能瓶颈,其中一个优化方法就是使用多线程。例如,注册了了3000个特征,每个特征4096个float。可以把3000个特征比对放到4个线程中进行计算,然…...

【测绘程序设计】——平面坐标转换

测绘工程中经常遇到平面坐标转换——比如,北京54(或西安80)平面坐标转换成CGCS2000平面坐标、工程独立坐标系平面坐标转换成CGCS2000平面坐标等,常用转换模型包括:①三参数法(2平移+1旋转);②四参数法(赫尔默特法,2平移+1旋转+1尺度);③六参数法(仿射变换法,2平移…...

五子棋的设计与实现

术:Java等摘要:五子棋是一种两人对弈的纯策略型棋类游戏,非常容易上手,老少皆宜。为了更好的推广五子棋,研究简单的人工智能方式,运用Java开发五子棋游戏。主要包含了人机对战,棋盘初始化&#…...

大数据项目软硬件选择

目录 一.技术选型 二.系统数据流程设计 三.框架版本选型 如何选择Apache/CDH/HDP版本...

redis数据结构的适用场景分析

1、String 类型的内存空间消耗问题,以及选择节省内存开销的数据类型的解决方案。 为什么 String 类型内存开销大? 图片 ID 和图片存储对象 ID 都是 10 位数,我们可以用两个 8 字节的 Long 类型表示这两个 ID。因为 8 字节的 Long 类型最大可以…...

同步、异步、全双工、半双工的区别

1、通讯 1.1 并行通讯 定义:一条信息的各位数据被同时传送的通讯方式称为并行通讯; 特点: 各个数据位同时发送,传送速度快、效率高,但有多少数据位就需要多少根数据线,因此传送成本高,并且只…...

ClickHouse 与 Amazon S3 结合?一起来探索其中奥秘

目录ClickHouse 简介ClickHouse 与对象存储ClickHouse 与 S3 结合的三种方法示例参考架构小结参考资料ClickHouse 简介ClickHouse 是一种快速的、开源的、用于联机分析(OLAP)的列式数据库管理系统(DBMS),由俄罗斯的Yan…...

【Spark分布式内存计算框架——Structured Streaming】1. Structured Streaming 概述

前言 Apache Spark在2016年的时候启动了Structured Streaming项目,一个基于Spark SQL的全新流计算引擎Structured Streaming,让用户像编写批处理程序一样简单地编写高性能的流处理程序。 Structured Streaming并不是对Spark Streaming的简单改进&#xf…...

【Windows】【Linux】---- Java证书导入

问题: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 无法找到请求目标的有效证书路径 一、Windows—java证书导入 1、下载证书到本地(以下…...

【Linux学习】菜鸟入门——gcc与g++简要使用

一、gcc/g gcc/g是编译器,gcc是GCC(GUN Compiler Collection,GUN编译器集合)中的C编译器;g是GCC中的C编译器。使用g编译文件时会自动链接STL标准库,而gcc不会自动链接STL标准库。下面简单介绍一下Linux环境下(Windows差…...

Cadence Allegro 导出Bill of Material Report详解

⏪《上一篇》   🏡《总目录》   ⏩《下一篇》 目录 1,概述2,Assigned Functions Report作用3,Assigned Functions Report示例4,Assigned Functions Report导出方法4.1,方法14.2,方法2B站关注“硬小二”浏览更多演示视频...

localStorage线上问题的思考

一、背景: localStorage作为HTML5 Web Storage的API之一,使用标准的键值对(Key-Value,简称KV)数据类型主要作用是本地存储。本地存储是指将数据按照键值对的方式保存在客户端计算机中,直到用户或者脚本主动清除数据&a…...

什么是DNS域名解析

什么是DNS域名解析?因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。通过主机名,得到该主机名对应的IP地址的过程叫做域名解析。正向解析&#xff1a…...

Cadence Allegro 导出Assigned Functions Report详解

⏪《上一篇》   🏡《总目录》   ⏩《下一篇》 目录 1,概述2,Assigned Functions Report作用3,Assigned Functions Report示例4,Assigned Functions Report导出方法4.1,方法14.2,方法2B站关注“硬小二”浏览更多演示视频...

Python中Opencv和PIL.Image读取图片的差异对比

近日,在进行深度学习进行推理的时候,发现不管怎么样都得不出正确的结果,再仔细和正确的代码进行对比了后发现原来是Python中不同的库读取的图片数组是有差异的。 image np.array(Image.open(image_file).convert(RGB)) image cv2.imread(…...

win10 WSL2 使用Ubuntu配置与安装教程

Win10 22H2ubuntu 22.04ROS2 文章目录一、什么是WSL2二、Win10 系统配置2.1 更新Windows版本2.2 Win10系统启用两个功能2.3 Win10开启BIOS/CPU开启虚拟化(VT)(很关键)2.4 下载并安装wsl_update_x64.msi2.5 PowerShell安装组件三、PowerShell安装Ubuntu3.…...

LeetCode每日一题(28. Find the Index of the First Occurrence in a String)

Given two strings needle and haystack, return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack. Example 1: Input: haystack “sadbutsad”, needle “sad” Output: 0 Explanation: “sad” occurs at index 0 and…...

Android 圆弧形 SeekBar

效果预览package com.gcssloop.widget;import android.annotation.SuppressLint;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Matrix;import android.graph…...

java 字典

java 字典 数据结构总览 Map Map 描述的是一种映射关系,一个 key 对应一个 value,可以添加,删除,修改和获取 key/value,util 提供了多种 Map HashMap: hash 表实现的 map,插入删除查找性能都是 O(1)&…...

【企业服务器LNMP环境搭建】mysql安装

MySQL安装步骤: 1、相关说明 1.1、编译参数的说明 -DCMAKE_INSTALL_PREFIX安装到的软件目录-DMYSQL_DATADIR数据文件存储的路径-DSYSCONFDIR配置文件路径 (my.cnf)-DENABLED_LOCAL_INFILE1使用localmysql客户端的配置-DWITH_PARTITION_STORAGE_ENGINE使mysql支持…...

vue自定义指令以及angular自定义指令(以禁止输入空格为例)

哈喽,小伙伴们,大家好啊,最近要实现一个vue自定义指令,就是让input输入框禁止输入空格建立一个directives的指令文件,里面专门用来建立各个指令的官方文档:自定义指令 | Vue.js (vuejs.org)我们都知道vue中…...

异常 复习

异常复习 异常(广义):泛指程序中一切不正常的情况 错误:例如内存不够用,程序是无法解决的 异常(狭义):程序在运行中出现问题,但是可以通过异常处理机制处理,程序可以继续向后执行 异常体系 Throwable类有两个直接子类:Excepti…...

K8s:开源安全平台 kubescape 实现 Pod 的安全合规检查/镜像漏洞扫描

写在前面 生产环境中的 k8s 集群安全不可忽略,即使是内网环境容器化的应用部署虽然本质上没有变化,始终是机器上的一个进程但是提高了安全问题的处理的复杂性分享一个开源的 k8s 集群安全合规检查/漏洞扫描 工具 kubescape博文内容涉及: kube…...

林州网站建设策划/直播发布会

今天的帖子是2014年1月的10个jQuery插件。注意:这些插件已在2014年1月制作或更新。希望您能找到有用的东西。 1. DropzoneJS 它是一个开放源代码库,提供带有图像预览的拖放文件上传。 来源演示 2. iView 世界上最出色的jQuery图像和内容滑块 来源演示 3.…...

山东网站建设方案制作/自己怎么免费做网站网页

在孙鑫老师第二节课视频上听到的,索性就记在这里了,省的下次又不知道怎么说了。在C语言中,struct结构体中是不能定义成员函数的,在C中可以。 但在C语言中可以定义函数指针变量来达到自己所要的目的,不顾这貌似也不太必…...

携程网站建设目的/在哪个平台做推广比较好

简介:SaltStack是 一个服务器基础架构集中化管理平台,具备配置管理、远程执行、监控等功能,一般可以理解为简化版的puppet和加强版的func。SaltStack基 于Python语言实现,结合轻量级消息队列(ZeroMQ)与Pyth…...

服务器免费/中山seo关键词

目录1. Chrome DevTools功能简介2. 使用Elements调试DOM2.1 查看编辑HTML和DOM2.2 在Console中访问节点2.3 在DOM中断点调试3. 调试样式及CSS3.1 查看和编辑CSS3.2 在元素中动态添加类与伪类3.3 快速调试CSS数值及颜色图形动画等4. 使用 Console 和 Sources 调试 JavaScript4.1…...

北京展览馆网站建设/免费的行情网站app

你有没有碰到过OpenStack中,VM失去IP地址的问题?如果有的话,你知道那可能是什么问题 ——特别是如果你拥有大量的节点和VM。你的客户会因为没有明显原因却断了与VM的连接而感到 挫败。甚至云的支持团队会为log文件里没有提示却出现问题感到挫…...

用文本文件做网站/媒体135网站

\Users\你的用户\.android\adb_usb.ini .android目录是隐藏的,需要开启隐藏目录显示。 打开文件后我的机器默认的是0x1949,估计应该都是这个。 在下面追加 kindlefire的: 0x0006 小米2的 : 0x2717 文件是这样的最后 -…...