FT2000+低温情况下RTC守时问题

1、背景介绍

飞腾2000+芯片通过I2C连接一块RTC时钟芯片(BellingBL5372)来实现麒麟信安系统下后的守时功能。目前BIOS支持UEFI功能,BIOS上电后能获取RTC时间,并将时间写入相应的UEFI变量或内存区域,操作系统上电后使用UEFI的APIs来读取相应的RTC时间变量或内存区域。

2、问题描述

在低温情况下(-42度),发现BIOS有概率无法获取到RTC时间,导致写入UEFI变量写入失败,从而操作系统也无法获取正确时间,变成系统出厂时间(2019年9月3日),如下图为出错情况:

 

但其实RTC芯片是正常工作的,当恢复到常温后又能获取到正确的时间,如下图

 

问题表现为低温情况下RTC守时不准。

3、原因分析

上电时BIOS通过I2C去访问RTC时钟芯片,由于FT2000+芯片商业档工作范围为0-70度,在低温-42度情况下完全有可能工作不正常,尤其是刚上电的那十几秒内,此时的I2C有概率无法读到RTC时钟芯片,导致BIOS时间无法获取,进一步导致系统下守时失败。

解决RTC问题其实就是解决一个温度问题,确保在温度满足芯片工作范围的情况下去读取RTC时钟芯片,也就是将读取时间后移,让芯片工作一段时间温度达到0度后再去读取。

对系统来说,有两种获取RTC时间的方式,一种就是从BIOS UEFI空间中去获取,另一种是调用系统下的驱动直接去读取时间。明显第二种方法是在操作系统启动过程中了,读取时间后移,能确保读到正确的时间。

4、问题解决

明确了通过加载驱动来获取RTC时间,那就按照这个思路去实现即可。

首先,在BIOS中添加RTC设备,通过dtb的方式添加,如下:

&i2c0 {status = "ok";rtc@32 {compatible = "beilin,bl5372";reg = <0x32>;status = "ok";};
};

这样驱动就能和设备匹配上,驱动代码如下:

/** An I2C driver for Beilin BL5372 RTC*/#include <linux/i2c.h>
#include <linux/bcd.h>
#include <linux/rtc.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/of.h>#define DEG 0#define DRV_VERSION "2.0"#define TIME24 0
#define RS5C_ADDR(R)		(((R) << 4) | 0)
#define RS5C372_REG_SECS	0
#define RS5C372_REG_MINS	1
#define RS5C372_REG_HOURS	2
#define RS5C372_REG_WDAY	3
#define RS5C372_REG_DAY		4
#define RS5C372_REG_MONTH	5
#define RS5C372_REG_YEAR	6
#define RS5C372_REG_TRIM	7
#define RS5C_REG_ALARM_A_MIN	8			/* or ALARM_W */
#define RS5C_REG_ALARM_A_HOURS	9
#define RS5C_REG_ALARM_A_WDAY	10#define RS5C_REG_ALARM_B_MIN	11			/* or ALARM_D */
#define RS5C_REG_ALARM_B_HOURS	12
#define RS5C_REG_ALARM_B_WDAY	13			/* (ALARM_B only) */
#define RS5C_REG_CTRL1		14
#define RS5C_REG_CTRL2		15
#define DEVICE_ADDR        0x32	//0x5dstatic unsigned rs5c_reg2hr(unsigned reg)
{
#if TIME24printk("<RTC> TIME24 0x%x\n",bcd2bin(reg & 0x3f));return bcd2bin(reg & 0x3f);
#elseunsigned	hour;printk("<RTC> TIME12 0x%x\n",bcd2bin(reg & 0x1f));hour = bcd2bin(reg & 0x1f);if (hour == 12)hour = 0;if (reg & 0x20)hour += 12;printk("<RTC> TIME12 hour=%d\n",hour);return hour;
#endif
}static unsigned rs5c_reg2mon(unsigned reg)
{
#if TIME24printk("<RTC> TIME24 0x%x\n",bcd2bin(reg & 0x3f));return bcd2bin(reg & 0x3f);
#elseunsigned	month;printk("<RTC> TIME12 0x%x\n",bcd2bin(reg & 0x1f));month = bcd2bin(reg & 0x1f);if (month > 12)month -= 12;printk("<RTC> TIME12 hour=%d\n",month);return month;
#endif
}static unsigned rs5c_hr2reg(unsigned hour)
{#if TIME24printk("<RTC> TIME24 0x%x\n",bin2bcd(hour));return bin2bcd(hour);#elseif (hour > 12){printk("<RTC> TIME12(>12) 0x%x\n",(0x20 | bin2bcd(hour - 12)));return 0x20 | bin2bcd(hour - 12);}if (hour == 12){printk("<RTC> TIME12(==12) 0x%x\n",(0x20 | bin2bcd(12)));return 0x20 | bin2bcd(12);}if (hour == 0){printk("<RTC> TIME12(==0) 0x%x\n",bin2bcd(12));return bin2bcd(12);}printk("<RTC> TIME12(<12) 0x%x\n",bin2bcd(hour));return bin2bcd(hour);
#endif
}//-----------------------------------------------
static struct i2c_driver bl5372_driver;struct bl5372 {struct rtc_device *rtc;struct device *dev;int irq;/*unsigned char sec;unsigned char min;unsigned char hour;unsigned char week;unsigned char day;unsigned char month;unsigned int year;*/
};static int i2c_write_bytes(struct i2c_client *client, uint8_t *data, uint16_t len)
{struct i2c_msg msg;int ret=-1;msg.flags = !I2C_M_RD;msg.addr = client->addr;msg.len = len;msg.buf = data;ret=i2c_transfer(client->adapter, &msg,1);return ret;
}static int bl5372_get_datetime(struct i2c_client *client, struct rtc_time *tm)
{struct bl5372 *bl5372 = i2c_get_clientdata(client);unsigned char buf[7] = { RS5C_ADDR(RS5C372_REG_SECS) };printk("<RTC>bsl5372_get_datetime\n");struct i2c_msg msgs[] = {{/* setup read ptr */.addr = client->addr,.flags = 0,/* write */.len = 1,.buf = buf},{/* read the sec,min,hour,week,day,month,year */.addr = client->addr,.flags = I2C_M_RD,/* read */.len = 7,.buf = buf},};//int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)//@num: Number of messages to be executed.//ÕâÀïÓÐÁœžöÏûÏ¢£¬ msgs[]µÄŽóС/* read registers */if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {dev_err(&client->dev, "%s: read error\n", __func__);return -EIO;}printk("<RTC> buf[2]=0x%x\n",buf[2]);tm->tm_sec = bcd2bin(buf[0] & 0x7f);tm->tm_min = bcd2bin(buf[1] & 0x7f);printk("<RTC> Get hour Label*****\n");tm->tm_hour = rs5c_reg2hr(buf[2]);tm->tm_mday = bcd2bin(buf[4] & 0x7f);tm->tm_wday = bcd2bin(buf[3] & 0x7f);printk("<RTC> Get month Label*****\n");//tm->tm_mon = rs5c_reg2hr(buf[5])-1;tm->tm_mon = rs5c_reg2mon(buf[5])-1;tm->tm_year = bcd2bin(buf[6] & 0x7f)+100;printk("<RTC>@GET1 year%d month%d mday%d wday%d hour%d min%d sec%d\n",tm->tm_year,tm->tm_mon,tm->tm_mday,tm->tm_wday,tm->tm_hour,tm->tm_min,tm->tm_sec);#if 1//------------------------------------buf[0]= RS5C_ADDR(RS5C_REG_CTRL2);struct i2c_msg msgs2[] = {{/* setup read  */.addr = client->addr,.len = 1,.buf = buf},{/* read is_24hour */.addr = client->addr,.flags = I2C_M_RD,.len = 1,.buf = buf},};/* read registers */if ((i2c_transfer(client->adapter, msgs2, 2)) != 2) {dev_err(&client->dev, "%s: read error\n", __func__);return -EIO;}if(buf[0]&0x20){printk("24小时\n");tm->tm_hour= (tm->tm_hour<24)? (tm->tm_hour):(24-tm->tm_hour);}else{tm->tm_hour=(tm->tm_hour<24)? (tm->tm_hour):(tm->tm_hour+24);//	tm->tm_mday=(tm->tm_mday<7)? (tm->tm_mday):(tm->tm_mday+8-24);//	tm->tm_hour=tm->tm_hour+8;}#endif//tm->tm_hour= (tm->tm_hour<24)? (tm->tm_hour):(tm->tm_hour-24);/* the clock can give out invalid datetime, but we cannot return* -EINVAL otherwise hwclock will refuse to set the time on bootup.*/printk("<RTC>@GET2 year%d month%d mday%d wday%d hour%d min%d sec%d\n",tm->tm_year,tm->tm_mon,tm->tm_mday,tm->tm_wday,tm->tm_hour,tm->tm_min,tm->tm_sec);if (rtc_valid_tm(tm) < 0)dev_err(&client->dev, "retrieved date/time is not valid.\n");return 0;
}static int bl5372_set_datetime(struct i2c_client *client, struct rtc_time *tm)
{struct bl5372 *bl5372 = i2c_get_clientdata(client);int i, err;unsigned char buf[7];printk("<RTC>####bl5372_set_datetime\n");
//------------------------------------buf[0]= RS5C_ADDR(RS5C_REG_CTRL2);struct i2c_msg msgs2[] = {{/* setup read  */.addr = client->addr,.len = 1,.buf = buf},{/* read is_24hour */.addr = client->addr,.flags = I2C_M_RD,.len = 1,.buf = buf},};/* read registers */if ((i2c_transfer(client->adapter, msgs2, 2)) != 2) {dev_err(&client->dev, "%s: read error\n", __func__);return -EIO;}/*	if((buf[0]&0x20)== 0){printk("RTC 12xiaoshi\n");rs5c_hr2regbuf[0] |= (1<<5);err = i2c_master_send(client, buf, 1);}
*/printk("<RTC>IN####year%d month%d mday%d wday%d hour%d min%d sec%d\n",tm->tm_year,tm->tm_mon,tm->tm_mday,tm->tm_wday,tm->tm_hour,tm->tm_min,tm->tm_sec);//------------------------/* hours, minutes and seconds */buf[0] = bin2bcd(tm->tm_sec);buf[1] = bin2bcd(tm->tm_min);printk("<RTC> Set hour Label*****\n");buf[2] = rs5c_hr2reg(tm->tm_hour);buf[3] = bin2bcd(tm->tm_wday & 0x07); //week 0~6buf[4] = bin2bcd(tm->tm_mday);buf[5] = bin2bcd(tm->tm_mon)+1;// 0~11tm->tm_year -= 100;buf[6] = bin2bcd(tm->tm_year % 100);// start at 1900  2018=>118
//
printk("###########write data to rtc \n");for(i=0;i<7;i++)
{
printk("buf[%d] is 0x%x\n",i,buf[i]);
}err = i2c_smbus_write_byte_data(client, RS5C_ADDR(RS5C372_REG_SECS),   buf[0]);i2c_smbus_write_byte_data(client, RS5C_ADDR(RS5C372_REG_MINS) ,  buf[1]);i2c_smbus_write_byte_data(client, RS5C_ADDR(RS5C372_REG_HOURS) , buf[2]);i2c_smbus_write_byte_data(client, RS5C_ADDR(RS5C372_REG_WDAY) ,  buf[3]);i2c_smbus_write_byte_data(client, RS5C_ADDR(RS5C372_REG_DAY) ,   buf[4]);i2c_smbus_write_byte_data(client, RS5C_ADDR(RS5C372_REG_MONTH) , buf[5]);i2c_smbus_write_byte_data(client, RS5C_ADDR(RS5C372_REG_YEAR) ,  buf[6]);return 0;
}#ifdef CONFIG_RTC_INTF_DEV
static int bl5372_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{struct bl5372 *bl5372 = i2c_get_clientdata(to_i2c_client(dev));struct rtc_time tm;switch (cmd) {case RTC_RD_TIME://bl5372_get_datetime(to_i2c_client(dev), &tm);return 0;case RTC_SET_TIME:if (copy_from_user(&tm, arg, sizeof(tm)))return -EFAULT;bl5372_set_datetime(to_i2c_client(dev), &tm);return 0;default:return -ENOIOCTLCMD;}}
#else
#define bl5372_rtc_ioctl NULL
#endifstatic int bl5372_rtc_read_time(struct device *dev, struct rtc_time *tm)
{return bl5372_get_datetime(to_i2c_client(dev), tm);
}static int bl5372_rtc_set_time(struct device *dev, struct rtc_time *tm)
{return bl5372_set_datetime(to_i2c_client(dev), tm);
}static int bl5372_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{struct bl5372 *bl5372 = i2c_get_clientdata(to_i2c_client(dev));return 0;
}static int bl5372_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{struct bl5372 *bl5372 = i2c_get_clientdata(to_i2c_client(dev));return 0;
}static int bl5372_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{//struct bl5372 *bl5372 = dev_get_drvdata(dev);struct bl5372 *bl5372 = i2c_get_clientdata(to_i2c_client(dev));return 0;
}static const struct rtc_class_ops bl5372_rtc_ops = {.ioctl		= bl5372_rtc_ioctl,.read_time	= bl5372_rtc_read_time,.set_time	= bl5372_rtc_set_time,.read_alarm        = bl5372_rtc_getalarm,.set_alarm         = bl5372_rtc_setalarm,.alarm_irq_enable  = bl5372_rtc_alarm_irq_enable
};static int bl5372_probe(struct i2c_client *client,const struct i2c_device_id *id)
{struct bl5372 *bl5372;if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)){return -ENODEV;}bl5372 = devm_kzalloc(&client->dev, sizeof(struct bl5372),GFP_KERNEL);if (!bl5372){return -ENOMEM;}device_init_wakeup(&client->dev, 1);i2c_set_clientdata(client, bl5372);bl5372->rtc = devm_rtc_device_register(&client->dev,bl5372_driver.driver.name,&bl5372_rtc_ops, THIS_MODULE);if (IS_ERR(bl5372->rtc)){return PTR_ERR(bl5372->rtc);}bl5372->rtc->uie_unsupported = 1;return 0;
}static int bl5372_remove(struct i2c_client *client)
{return 0;
}static const struct i2c_device_id bl5372_id[] = {{ "bl5372", 0 },{ }
};
MODULE_DEVICE_TABLE(i2c, bl5372_id);#ifdef CONFIG_OF
static const struct of_device_id bl5372_of_match[] = {{ .compatible = "beilin,bl5372" },{}
};
MODULE_DEVICE_TABLE(of, bl5372_of_match);
#endifstatic struct i2c_driver bl5372_driver = {.driver		= {.name	= "rtc-bl5372",.owner	= THIS_MODULE,.of_match_table = of_match_ptr(bl5372_of_match),},.probe		= bl5372_probe,.remove		= bl5372_remove,.id_table	= bl5372_id,
};module_i2c_driver(bl5372_driver);MODULE_AUTHOR("Zhengweiqing <1548889230@qq.com>");
MODULE_DESCRIPTION("Beilin BL5372 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);

下面是Makefile

obj-m +=rtc_bl5372.oKDIR=/lib/modules/$(shell uname -r)/buildall:make -C $(KDIR) M=$(PWD) modules
clean:make -C $(KDIR) M=$(PWD) clean

编译成ko,然后包进内核里面

将驱动编到内核里面后重新上电能发现系统下面有两个rtc设备,其中rtc0是系统采用BIOS UEFI方式产生的,rtc1是系统加载驱动产生的,如下图

 

由于系统时间默认采用的是从rtc0获取的时间,需要改为从rtc1(通过加载驱动产生的设备)获取时间。

在/etc/udev/rules.d下创建文件,这里命名为rtc1.rules

 

文件内容如下:

KERNEL=="rtc0",SYMLINK+="rtc_old"KERNEL=="rtc1",SYMLINK+="rtc"

 

上述规则将原来命名为rtc0的设备链接到rtc old,将rtc1设备链接到rtc。这样,系统会将rtc1设备作为默认的时钟设备。

然后重新加载udev规则,使修改生效

udevadm control --reload-rules

重启系统即可。

重启后输入dmesg能看到后面又多出来一段内容,那就是配置系统时间时读取rtc1时间了:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/98665.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

antd5源码调试环境启动(MacOS)

将源码下载至本地 这里antd5 版本是5.8.3 $ git clone gitgithub.com:ant-design/ant-design.git $ cd ant-design $ npm install $ npm start前提&#xff1a;安装python3、node版本18.14.0(这是本人当前下载的版本&#xff09; python3安装教程可参考&#xff1a;https://…

Vue3 用父子组件通信实现页面页签功能

一、大概流程 二、用到的Vue3知识 1、组件通信 &#xff08;1&#xff09;父给子 在vue3中父组件给子组件传值用到绑定和props 因为页签的数组要放在父页面中&#xff0c; data(){return {tabs: []}}, 所以顶部栏需要向父页面获取页签数组 先在页签页面中定义props用来接…

Docker 常规软件安装

1. 总体安装步骤 1. 搜索镜像 search 2. 拉取镜像 pull 3. 查看镜像 images 4. 启动镜像 - 端口映射 run 5. 停止容器 stop 6. 移除容器 rm 2. 安装tomcat 1. 搜索 docker search tomcat 2. 拉取 docker pull tomcat 3. 查看本地镜像 docker images tomcat 4. 创建容器实…

搭载KaihongOS的工业平板、机器人、无人机等产品通过3.2版本兼容性测评,持续繁荣OpenHarmony生态

近日&#xff0c;搭载深圳开鸿数字产业发展有限公司&#xff08;简称“深开鸿”&#xff09;KaihongOS软件发行版的工业平板、机器人、无人机等商用产品均通过OpenAtom OpenHarmony&#xff08;以下简称“OpenHarmony”&#xff09;3.2 Release版本兼容性测评&#xff0c;获颁O…

漏洞指北-VulFocus靶场专栏-中级01

漏洞指北-VulFocus靶场专栏-中级01 中级001 &#x1f338;dcrcms 文件上传 &#xff08;CNVD-2020-27175)&#x1f338;step1&#xff1a;输入账号 密码burp suite 拦截 修改类型为 jpeg 中级002 &#x1f338;thinkphp3.2.x 代码执行&#x1f338;step1&#xff1a;burpsuite …

《HeadFirst设计模式(第二版)》第十一章代码——代理模式

代码文件目录&#xff1a; RMI&#xff1a; MyRemote package Chapter11_ProxyPattern.RMI;import java.rmi.Remote; import java.rmi.RemoteException;public interface MyRemote extends Remote {public String sayHello() throws RemoteException; }MyRemoteClient packa…

Java之优雅处理 NullPointerException空指针异常

前言 NPE问题就是&#xff0c;我们在开发中经常碰到的NullPointerException。假设我们有两个类&#xff0c;他们的UML类图如下图所示 在这种情况下&#xff0c;有如下代码 user.getAddress().getProvince(); 这种写法&#xff0c;在user为null时&#xff0c;是有可能报Nul…

网络编程面试笔试题

一、OSI 7层模型&#xff0c;TCP/IP 4层模型 5层模型。 以及每一层的功能&#xff08;重点&#xff1a;第三层 第四层&#xff09; 答&#xff1a; 7层模型&#xff08;①物理层&#xff1a;二进制比特流传输&#xff0c;②数据链路层&#xff1a;相邻结点的可靠传输&#xf…

奇舞周刊第503期:图解串一串 webpack 的历史和核心功能

记得点击文章末尾的“ 阅读原文 ”查看哟~ 下面先一起看下本期周刊 摘要 吧~ 奇舞推荐 ■ ■ ■ 图解串一串 webpack 的历史和核心功能 提到打包工具&#xff0c;可能你会首先想到 webpack。那没有 webpack 之前&#xff0c;都是怎么打包的呢&#xff1f;webpack 都有哪些功能&…

selenium 选定ul-li下拉选项中某个指定选项

场景&#xff1a;selenium的下拉选项是ul-li模式&#xff0c;选定某个指定的选项。 from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 显示等待def select_li(self, text, *ul_locator):"…

贝叶斯公式

一、贝叶斯公式 贝叶斯公式是一种用于概率推断的重要数学工具&#xff0c;它描述了在观测到新信息后如何更新关于某个事件的概率分布。贝叶斯公式的一般形式如下&#xff1a; P(A∣B)P(B∣A)⋅P(A) ​/ P(B) 其中&#xff1a; P(A∣B) 表示在给定观测到事件 B 后&#xff0c…

[RDMA] 高性能异步的消息传递和RPC :Accelio

1. Introduce Accelio是一个高性能异步的可靠消息传递和RPC库&#xff0c;能优化硬件加速。 RDMA和TCP / IP传输被实现&#xff0c;并且其他的传输也能被实现&#xff0c;如共享存储器可以利用这个高效和方便的API的优点。Accelio 是 Mellanox 公司的RDMA中间件&#xff0c;用…

分布式 | 如何搭建 DBLE 的 JVM 指标监控系统

本篇文章采用 Docker 方式搭建 Grafana Prometheus 实现对 DBLE 的 JVM 相关指标的监控系统。 作者&#xff1a;文韵涵 爱可生 DBLE 团队开发成员&#xff0c;主要负责 DBLE 需求开发&#xff0c;故障排查和社区问题解答。 本文来源&#xff1a;原创投稿 爱可生开源社区出品&a…

python中使用xml快速创建Caption和URL书签管理器应用程序

导语&#xff1a; 本文介绍如何使用wxPython库创建一个Caption和URL管理器应用程序。该应用程序具有图形用户界面&#xff0c;允许用户输入Caption和URL&#xff0c;并将其保存到XML文件中。此外&#xff0c;还提供了浏览文件夹并选择HTML文件的功能&#xff0c;并可以运行另一…

安装jenkins-cli

1、要在 Linux 操作系统上安装 jcli curl -L https://github.com/jenkins-zh/jenkins-cli/releases/latest/download/jcli-linux-amd64.tar.gz|tar xzv sudo mv jcli /usr/local/bin/ 在用户根目录下&#xff0c;增加 jcli 的配置文件&#xff1a; jcli config gen -ifalse …

回归预测 | MATLAB实现BO-SVM贝叶斯优化支持向量机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现BO-SVM贝叶斯优化支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现BO-SVM贝叶斯优化支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果一览基本介绍程序设计…

构建 NodeJS 影院微服务并使用 docker 部署它(02/4)

一、说明 构建一个微服务的电影网站&#xff0c;需要Docker、NodeJS、MongoDB&#xff0c;这样的案例您见过吗&#xff1f;如果对此有兴趣&#xff0c;您就继续往下看吧。 图片取自网络 — 封面由我制作 这是✌️“构建 NodeJS 影院微服务”系列的第二篇文章。 二、对第一部分的…

对象内存布局与对象头

对象内存布局 在Hotspot虚拟机里&#xff0c;对象在堆内存中的存储布局可以划分为三个部分&#xff1a;对象头、实例数据、对齐填充。 对象头 在64为系统中&#xff0c;Mark word 占了8个字节&#xff0c;cla ss Pointer 占了8个字节。从jdk1.8开始&#xff0c;存在指针压缩&am…

【学习日记】【FreeRTOS】FreeRTOS 移植到 STM32F103C8

前言 本文基于野火 FreeRTOS 教程&#xff0c;内容是关于 FreeRTOS 官方代码的移植的注意事项&#xff0c;并将野火例程中 STM32F103RC 代码移植到 STM32F103C8。 一、FreeRTOS V9.0.0 源码的获取 两个下载链接&#xff1a; 官 网 代码托管 二、源码文件夹内容简介 Source…

线程基础和CompletableFuture异步编排

目录 一、线程回顾 1、初始化线程的 4 种方式 2、线程池的七大参数 3、常见的 4 种线程池 4、开发中为什么使用线程池 二、CompletableFuture 异步编排 1、创建异步对象 2、计算完成时回调方法 3、handle 方法 4、线程串行化方法 5、两任务组合 - 都要完成 6、两任务…