Java多线程--线程间的通信
文章目录
- 一、线程间的通信
- (1)为什么要处理线程间的通信
- (2)等待唤醒机制
- 二、案例
- (1)案例
- 1、创建线程
- 2、解决线程安全问题
- 3、等待
- 4、唤醒
- 5、同步监视器
- (2)调用wait和notify需注意的细节
- 三、wait与sleep的区别
一、线程间的通信
(1)为什么要处理线程间的通信
当我们需要多个线程
来共同完成一件任务,并且我们希望他们有规律的执行
,那么多线程之间需要一些通信机制,可以协调它们的工作,以此实现多线程共同操作一份数据。(在同步的基础之上解决通信的问题)
比如:线程A用来生产包子的,线程B用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的动作,一个是生产,一个是消费,此时B线程必须等到A线程完成后才能执行,那么线程A与线程B之间就需要线程通信,即—— 等待唤醒机制。
(2)等待唤醒机制
这是多个线程间的一种协作机制
。
谈到线程我们经常想到的是线程间的竞争(race)
,比如去争夺锁,但这并不是故事的全部,线程间也会有协作机制。
在一个线程满足某个条件时,就进入等待状态(wait() / wait(time)
), 等待其他线程执行完他们的指定代码过后再将其唤醒(notify()
);
或可以指定wait的时间,等时间到了自动唤醒;
在有多个线程进行等待时,如果需要,可以使用 notifyAll()
来唤醒所有的等待线程。wait/notify
就是线程间的一种协作机制。
- wait:线程不再活动,不再参与调度,进入
wait set
中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时的线程状态是 WAITING 或 TIMED_WAITING。它还要等着别的线程执行一个特别的动作
,也即“通知(notify)
”或者等待时间到,在这个对象上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue
)中。 - notify:则选取所通知对象的 wait set 中的一个线程释放。
- notifyAll:则释放所通知对象的 wait set 上的全部线程。
🗳️注意:
被通知的线程被唤醒后也不一定能立即恢复执行,因为它当初中断的地方是在同步块内,而此刻它已经不持有锁,所以它需要再次尝试去获取锁(很可能面临其它线程的竞争),成功后才能在当初调用 wait 方法之后的地方恢复执行。
总结如下:
- 如果能获取锁,线程就从 WAITING 状态变成 RUNNABLE(可运行) 状态;
- 否则,线程就从 WAITING 状态又变成 BLOCKED(等待锁) 状态
二、案例
(1)案例
🌋案例描述:
使用两个线程打印 1-100。线程1, 线程2 交替打印。
🍰分析
1、创建线程
这里使用实现的方式创建线程。
class PrintNumber implements Runnable{//共享100private int number=1;@Overridepublic void run() {}
}
在run
方法里面写上逻辑代码:
class PrintNumber implements Runnable{//共享100private int number=1;@Overridepublic void run() {//打印数据while(true){if(number<=100){System.out.println(Thread.currentThread().getName()+":"+number);number++;}else{break;}}}
}
在main
方法中创建两个线程,如下:
public class PrintNumberTest {public static void main(String[] args) {//创建PrintNumber类的实例PrintNumber p=new PrintNumber();Thread t1=new Thread(p,"线程1");Thread t2=new Thread(p,"线程2");t1.start();t2.start();}
}
关于number的问题,需要考虑同步。
在if中加一个sleep
,将问题放大。
目前的代码如下:
public class PrintNumberTest {public static void main(String[] args) {//创建PrintNumber类的实例PrintNumber p=new PrintNumber();Thread t1=new Thread(p,"线程1");Thread t2=new Thread(p,"线程2");t1.start();t2.start();}
}class PrintNumber implements Runnable{//共享100private int number=1;@Overridepublic void run() {//打印数据while(true){if(number<=100){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+":"+number);number++;}else{break;}}}
}
输出:
可以看到打印出了错误信息。
2、解决线程安全问题
现在我们使用同步代码块
来解决线程安全问题。
将操作number的代码用synchronized
包裹起来,就是下面蓝色部分:
用synchronized
包裹的时候,不能包裹少了,那样会有安全问题。
若是包裹多了,串行的场景就会更多,导致效率变低。
所以,操作共享数据的代码,不能包裹多,也不能少。
🌱代码
package yuyi04.Communication;/*** ClassName: PrintNumberTest* Package: yuyi04.Communication* Description:* 使用两个线程打印 1-100。线程1, 线程2 交替打印。* @Author 雨翼轻尘* @Create 2024/2/2 0002 14:57*/
public class PrintNumberTest {public static void main(String[] args) {//创建PrintNumber类的实例PrintNumber p=new PrintNumber();Thread t1=new Thread(p,"线程1");Thread t2=new Thread(p,"线程2");t1.start();t2.start();}
}class PrintNumber implements Runnable{//共享100private int number=1;@Overridepublic void run() {//打印数据while(true){synchronized (this) { //当前this表示PrintNumber的实例,即p,是唯一的if(number<=100){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+":"+number);number++;}else{break;}}}}
}
🍺输出
可以看到现在的线程是安全的。
3、等待
紧接着需要考虑下一个问题,需要将两个线程交替打印。
🎲如何实现交替?
比如线程1进入同步代码块,然后打印结束,出了同步代码块。
此时线程1需要进入阻塞状态,让线程2进入同步代码块输出执行,才能保证与线程1交替执行。
如何让线程1处于阻塞,这里需要使用一个方法wait()
,让它处于等待状态。如下:
线程一旦执行wait()
方法,就进入等待状态(阻塞),同时会释放对同步监视器的调用。
wait()
方法有一个异常需要处理一下:
注意,sleep()
不会释放同步监视器,这一点需要注意。
有时候面试会问,wait()
与sleep()
有什么区别?
这就是其中一个区别,sleep()
不会释放对同步监视器的调用,而wait()
会释放对同步监视器的调用。
🌱代码
public class PrintNumberTest {public static void main(String[] args) {//创建PrintNumber类的实例PrintNumber p=new PrintNumber();Thread t1=new Thread(p,"线程1");Thread t2=new Thread(p,"线程2");t1.start();t2.start();}
}class PrintNumber implements Runnable{//共享100private int number=1;@Overridepublic void run() {//打印数据while(true){synchronized (this) { //当前this表示PrintNumber的实例,即p,是唯一的if(number<=100){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+":"+number);number++;try {wait(); //线程一旦执行此方法,就进入等待状态(阻塞),同时会释放对同步监视器的调用} catch (InterruptedException e) {e.printStackTrace();}}else{break;}}}}
}
🍺输出
☕注意
一旦线程1进入同步代码块执行到wait()
,就会释放对同步监视器的调用,然后开始等待操作。
线程2就可以拿着同步监视器进入同步代码块了,当线程2执行到wait()
,它也开始等待。
所以两个线程都处于等待状态,只能输出两个数字,然后进入了阻塞状态。
4、唤醒
🎲如何不一直等待下去呢?
Ctrl+P
可以看到wait()
方法还有带参的,可以设置等待时间,就是达到时间自动醒来。如下:
现在我们不使用这个了,因为想让它们交互输出,可以考虑唤醒线程。
线程1进入同步代码块执行到wait()
,然后释放同步监视器,线程2进入同步代码块。
所以我们需要在线程2wait()
之前把线程1给唤醒。
可以在这里写上notify()
,如下:
🍰分析一下现在的情况:
线程1进入同步代码块,碰到了notify()
,发现没有可以唤醒的线程,就继续往后执行。
然后线程1打印了“1”,接下来执行到wait()
,开始等待,并且释放同步监视器。
线程2拿到锁,进入同步代码块,碰到了notify()
,发现线程1在等待,就将线程1叫醒,虽然叫醒了,但是没有用,因为已经没有同步资源了。
线程2就拿着锁继续执行,即使sleep
也不影响自动睡醒之后继续往后执行(sleep不会导致锁被释放),然后输出“2”。
当线程2执行到wait()
,就开始等待了,同时也释放同步监视器。
此时线程1是醒着的状态,从被wait()
的地方继续往后执行,后边要是有代码的话还需要继续执行,然后拿着锁再次进入同步代码块,然后碰到notify()
,将线程2叫醒。
🌱代码
package yuyi04.Communication;/*** ClassName: PrintNumberTest* Package: yuyi04.Communication* Description:* 使用两个线程打印 1-100。线程1, 线程2 交替打印。* @Author 雨翼轻尘* @Create 2024/2/2 0002 14:57*/
public class PrintNumberTest {public static void main(String[] args) {//创建PrintNumber类的实例PrintNumber p=new PrintNumber();Thread t1=new Thread(p,"线程1");Thread t2=new Thread(p,"线程2");t1.start();t2.start();}
}class PrintNumber implements Runnable{//共享100private int number=1;@Overridepublic void run() {//打印数据while(true){synchronized (this) { //当前this表示PrintNumber的实例,即p,是唯一的notify();if(number<=100){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+":"+number);number++;try {wait(); //线程一旦执行此方法,就进入等待状态(阻塞),同时会释放对同步监视器的调用} catch (InterruptedException e) {e.printStackTrace();}}else{break;}}}}
}
🍺输出(部分)
现在看到的就是交互的场景。
5、同步监视器
🗳️关于同步监视器
这里其实省略了this
,如下:
wait
同理:
notify(); //this.notify();
wait(); //this.wait();
凡是在方法当中,没有写是谁调用的,如果是非静态方法,那就少了this。
若是静态方法,那就是当前类。
这里的this
必须是一样的吗?如下:
我们先来自己定义一个同步监视器,然后将obj
放入:
🌱代码
public class PrintNumberTest {public static void main(String[] args) {//创建PrintNumber类的实例PrintNumber p=new PrintNumber();Thread t1=new Thread(p,"线程1");Thread t2=new Thread(p,"线程2");t1.start();t2.start();}
}class PrintNumber implements Runnable{//共享100private int number=1;Object obj=new Object();@Overridepublic void run() {//打印数据while(true){synchronized (obj) { //obj是唯一的,线程安全this.notify(); //notify();if(number<=100){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+":"+number);number++;try {wait(); //线程一旦执行此方法,就进入等待状态(阻塞),同时会释放对同步监视器的调用} catch (InterruptedException e) {e.printStackTrace();}}else{break;}}}}
}
🍺输出
会报一个异常IllegalMonitorStateException
,这表示当前使用的同步监视器(即obj)和notify()
、wait()
方法的调用者不一致。
所以,它们的调用者,必须是同步监视器。(这里也可以看出,notify
和wait
方法必须要在同步代码块或同步方法中使用,只有这两个结构中才存在同步监视器,Lock里面没有同步监视器)
🚗处理
既然现在的同步监视器是obj,那么就用obj去调用notify
和wait
方法即可。
如下:
🌱代码
package yuyi04.Communication;/*** ClassName: PrintNumberTest* Package: yuyi04.Communication* Description:* 使用两个线程打印 1-100。线程1, 线程2 交替打印。* @Author 雨翼轻尘* @Create 2024/2/2 0002 14:57*/
public class PrintNumberTest {public static void main(String[] args) {//创建PrintNumber类的实例PrintNumber p=new PrintNumber();Thread t1=new Thread(p,"线程1");Thread t2=new Thread(p,"线程2");t1.start();t2.start();}
}class PrintNumber implements Runnable{//共享100private int number=1;Object obj=new Object();@Overridepublic void run() {//打印数据while(true){synchronized (obj) { //obj是唯一的,线程安全obj.notify(); //notify();if(number<=100){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+":"+number);number++;try {obj.wait(); //线程一旦执行此方法,就进入等待状态(阻塞),同时会释放对同步监视器的调用} catch (InterruptedException e) {e.printStackTrace();}}else{break;}}}}
}
🍺输出(部分)
notify
和wait
方法的调用者必须是“同步监视器”。
(2)调用wait和notify需注意的细节
上述案例涉及到三个方法的使用:
wait()
:线程一旦执行此方法,就进入等待状态。同时,会释放对同步监视器的调用。notify()
:一旦执行此方法,就会唤醒被wait()
的线程中优先级最高的那一个线程。(如果被wait()
的多个线程的优先级相同,则随机唤醒一个)。被唤醒的线程从当初被wait的位置继续执行。notifyAll()
:一旦执行此方法,就会唤醒所有被wait的线程。
🗳️注意点
- 此三个方法的使用,必须是在同步代码块或同步方法(即
synchronized
结构)中。(超纲:Lock
需要配合Condition
实现线程间的通信,方式更加灵活) - 此三个方法的调用者,必须是同步监视器。否则,会报
IllegalMonitorStateException
异常。 - 此三个方法声明在Object类中。(当初说同步监视器的时候,说到“任何一个对象都可以来充当同步监视器”,那么就意味着任何一个对象都应该有能力去调用这几个方法,这几个方法必定是定义在Object类里面的)
☕调用wait
和notify
需注意的细节总结
1、wait
方法与notify
方法必须要由同一个锁对象调用
。
因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。
2、wait
方法与notify
方法是属于Object类的方法的。
因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。
3、wait
方法与notify
方法必须要在同步代码块
或者是同步函数
中使用。
因为:必须要通过锁对象
调用这2个方法。否则会报java.lang.IllegalMonitorStateException
异常。
三、wait与sleep的区别
🎲wait()
和 sleep()
的区别?(常见面试题)
①相同点:一旦执行,都会使得当前线程结束执行状态,进入阻塞状态。
②不同点:
- 声明的位置(定义方法所属的类)
wait()
:声明在Object类中。sleep()
:声明在Thread类中,静态的。
- 使用的场景不同(使用范围不同)
wait()
:只能使用在同步代码块或同步方法中。sleep()
:可以在任何需要使用的场景。
- 都在同步结构(同步代码块或同步方法)中使用的时候,是否释放同步监视器的操作不同
wait()
:一旦执行,会释放同步监视器。sleep()
:一旦执行,不会释放同步监视器。
- 结束阻塞的方式(结束等待的方式不同)
wait()
:到达指定时间自动结束阻塞 或 无限等待直到被notify/notifyAll唤醒,结束阻塞。sleep()
:到达指定时间自动结束阻塞。
相关文章:

Java多线程--线程间的通信
文章目录 一、线程间的通信(1)为什么要处理线程间的通信(2)等待唤醒机制 二、案例(1)案例1、创建线程2、解决线程安全问题3、等待4、唤醒5、同步监视器 (2)调用wait和notify需注意的…...
vue + element 页面滚动计算百分比 + 节流函数
html: <el-progress :percentage"scrollValue"></el-progress> js: data() {return {scrollValue: 0,} }, mounted() {window.addEventListener(scroll, this.handleScroll) // 监听页面滚动 }, beforeDestroy() {window.remov…...

【笔记】React Native实战练习(仿网易云游戏网页移动端)
/** * 如果系统看一遍RN相关官方文档,可能很快就忘记了。一味看文档也很枯燥无味, * 于是大概看了关键文档后,想着直接开发一个Demo出来,边学边写,对往后工作 * 开发衔接上能够更顺。这期间肯定会遇到各种各样的问题&a…...

Android SystemUI 介绍
目录 一、什么是SystemUI 二、SystemUI应用源码 三、学习 SystemUI 的核心组件 四、修改状态与导航栏测试 本篇文章,主要科普的是Android SystemUI , 下一篇文章我们将介绍如何把Android SystemUI 应用转成Android Studio 工程项目。 一、什么是Syst…...

2024美赛数学建模A题思路分析 - 资源可用性和性别比例
1 赛题 问题A:资源可用性和性别比例 虽然一些动物物种存在于通常的雄性或雌性性别之外,但大多数物种实质上是雄性或雌性。虽然许多物种在出生时的性别比例为1:1,但其他物种的性别比例并不均匀。这被称为适应性性别比例的变化。例…...

2024年数学建模美赛C题(预测 Wordle)——思路、程序总结分享
1: 问题描述与要求 《纽约时报》要求您对本文件中的结果进行分析,以回答几个问题。 问题1:报告结果的数量每天都在变化。开发一个模型来解释这种变化,并使用您的模型为2023年3月1日报告的结果数量创建一个预测区间。这个词的任何属性是否会…...

TryHackMe-File Inclusion练习
本文相关的TryHackMe实验房间链接:TryHackMe | Why Subscribe 路径遍历(目录遍历) LocationDescription/etc/issue包含要在登录提示之前打印的消息或系统标识。/etc/profile控制系统范围的默认变量,例如导出(Export)变量、文件创…...
Leetcode 《面试经典150题》169. 多数元素
题目 给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 输入:nums [3,2,3] 输出:3示…...

百度输入法往选字框里强塞广告
关注卢松松,会经常给你分享一些我的经验和观点。 国内几乎100%的输入法都有广告,只是你们没发现而已!!! 百度输入法居然在输入法键盘上推送广告,近日,博主阑夕 表示,V2EX论坛上有…...
分享一个Qt使用的模块间通信类
需求: 不同线程,或者同一线程的不同类之间通信,按照Qt的机制,定义一个信号,一个槽,然后绑定。以两个类A,B为例,A触发一个信号,B执行一个槽,在定义好信号和槽之后&#x…...

工作七年,对消息推送使用的一些经验和总结
前言:不管是APP还是WEB端都离不开消息推送,尤其是APP端,push消息,小信箱消息;WEB端的代办消息等。因在项目中多次使用消息推送且也是很多项目必不可少的组成部分,故此总结下供自己参考。 一、什么是消息推…...

计网——应用层
应用层 应用层协议原理 网络应用的体系结构 客户-服务器(C/S)体系结构 对等体(P2P)体系结构 C/S和P2P体系结构的混合体 客户-服务器(C/S)体系结构 服务器 服务器是一台一直运行的主机,需…...

算法面试八股文『 基础知识篇 』
博客介绍 近期在准备算法面试,网上信息杂乱不规整,出于强迫症就自己整理了算法面试常出现的考题。独乐乐不如众乐乐,与其奖励自己,不如大家一起嗨。以下整理的内容可能有不足之处,欢迎大佬一起讨论。 PS:…...

docker-学习-4
docker学习第四天 docker学习第四天1. 回顾1.1. 容器的网络类型1.2. 容器的本质1.3. 数据的持久化1.4. 看有哪些卷1.5. 看卷的详细信息 2. 如何做多台宿主机里的多个容器之间的数据共享2.1. 概念2.2. 搭NFS服务器实现多个容器之间的数据共享的详细步骤2.3. 如果是多台机器&…...

el-upload子组件上传多张图片(上传为files或base64url)
场景: 在表单页,有图片需要上传,表单的操作行按钮中有上传按钮,点击上传按钮。 弹出el-dialog进行图片的上传,可以上传多张图片。 由于多个表单页都有上传多张图片的操作,因此将上传多图的el-upload定义…...
2024美赛数学建模C题思路源码——网球选手的动量
这题挺有意思,没具体看比赛情况,打过比赛的人应该都知道险胜局(第二局、第五局逆转局)最影响心态的,导致第3、5局输了 模型结果需要证明这样的现象 赛题目的 赛题目的:分析网球球员的表现 问题一.球员在比赛特定时间表现力 问题分析 excel数据:每个时间段有16场比赛,…...

金三银四_程序员怎么写简历_写简历网站
你们在制作简历时,是不是基本只关注两件事:简历模板,还有基本信息的填写。 当你再次坐下来更新你的简历时,可能会发现自己不自觉地选择了那个“看起来最好看的模板”,填写基本信息,却没有深入思考如何使简历更具吸引力。这其实是一个普遍现象:许多求职者仍停留在传统简历…...

echarts条形图添加滚动条
效果展示: 测试数据: taskList:[{majorDeptName:测试,finishCount:54,notFinishCount:21}, {majorDeptName:测试,finishCount:54,notFinishCount:21}, {majorDeptName:测试,finishCount:54,notFinishCount:21}, {majorDeptName:测试,finishCount:54,notFinishCount:21}, {maj…...
Java 使用Soap方式调用WebService接口
pom文件依赖 <dependencies><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.13.0</version></dependency><!-- https://mvnrepository.com/artif…...
2024美赛数学建模所有题目思路分析
美赛思路已更新,关注后可以获取更多思路。并且领取资料 C题思路 首先,我们要理解势头是什么。简单来说,势头是一方在比赛中因一系列事件而获得的动力或优势。在网球中,这可能意味着连续赢得几个球,或是在比赛的某个关…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...