JUC并发编程——JUC并发编程概述及Lock锁(重点)(基于狂神说的学习笔记)

基于bilibili狂神说JUC并发编程视频所做笔记

概述

什么是JUC

JUC时java.util工具包中的三个包的简称

java.util.concurrent

java.util.concurrent.atomic

java.util.concurrent.locks

业务:普通的线程代码中,我们常使用Runnable接口

但Runnable没有返回值,且效率相比较于Callable来说相对较低,功能也没有Callable强大

线程和进程

进程:相当于一个程序

一个进程当中往往可以包含多个线程,且至少包含一个线程

Java默认有2个线程:mian(主线程),GC(垃圾回收)

Java真的可以开启线程吗?

java是无法开启线程的,Java运行在JVM(虚拟机)之上,无法直接操作硬件,因此其实际上是无法开启线程的,在我们无论使用Runnable接口还是继承Thread,用start()方法开启线程,其本质上都是调用==private native void start0()==方法,而该方法是本地方法,是运行底层的C++

并发编程:

并发与并行:

并发:(多个线程同时操作一个核)

  • CPU一核,快速交替轮换,模拟多核

并行:(多个线程操作多个核)

  • CPU多核,可以多个线程同时执行;引入线程池的概念

查看自己CPU核数:

public class Test1 {public static void main(String[] args) {// 获取CPU的核数// COU 密集型,IO密集型System.out.println(Runtime.getRuntime().availableProcessors());}
}

并发编程的本质:充分利用CPU的资源

线程有几个状态

答: 6个,分别为:NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED

public enum State{/*** Thread state for a thread which has not yet started.*/
NEW,/*** Thread state for a runnable thread.  A thread in the runnable* state is executing in the Java virtual machine but it may* be waiting for other resources from the operating system* such as processor.*/
RUNNABLE,/*** Thread state for a thread blocked waiting for a monitor lock.* A thread in the blocked state is waiting for a monitor lock* to enter a synchronized block/method or* reenter a synchronized block/method after calling* {@link Object#wait() Object.wait}.*/
BLOCKED,/*** Thread state for a waiting thread.* A thread is in the waiting state due to calling one of the* following methods:* <ul>*   <li>{@link Object#wait() Object.wait} with no timeout</li>*   <li>{@link #join() Thread.join} with no timeout</li>*   <li>{@link LockSupport#park() LockSupport.park}</li>* </ul>** <p>A thread in the waiting state is waiting for another thread to* perform a particular action.** For example, a thread that has called {@code Object.wait()}* on an object is waiting for another thread to call* {@code Object.notify()} or {@code Object.notifyAll()} on* that object. A thread that has called {@code Thread.join()}* is waiting for a specified thread to terminate.*/
WAITING,/*** Thread state for a waiting thread with a specified waiting time.* A thread is in the timed waiting state due to calling one of* the following methods with a specified positive waiting time:* <ul>*   <li>{@link #sleep Thread.sleep}</li>*   <li>{@link Object#wait(long) Object.wait} with timeout</li>*   <li>{@link #join(long) Thread.join} with timeout</li>*   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>*   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>* </ul>*/
TIMED_WAITING,/*** Thread state for a terminated thread.* The thread has completed execution.*/
TERMINATED;
}

WAITING 与 TIMED_WAITING的区别为:

WAITING会一直等待唤醒,或其他线程,资源的响应

TIMED_WAITING为超时等待,一旦时间到,则不再等待

wait/sleep的区别

  1. 来自不同的类

wait 来自Object类

sleep来自Thread类

  1. 关于锁的释放

wait会释放锁

sleep不会释放锁,抱着锁睡觉

  1. 使用的范围是不同的

wait 必须在同步代码块中使用

sleep 可以在任何地方睡

  1. 是否需要捕获异常

wait不需要捕获异常

sleep必须要捕获异常

(但是,只要与线程有关的操作,都要捕获中断异常)

Lock锁(重点)

回顾synchronized

传统Synchronized

package syn;// OOP并发编程
public class SaleTicketDemo01 {public static void main(String[] args) {// 声明一个票对象,使3个线程可以调用买票方法Ticket ticket = new Ticket();// 使用lambda表达式,回顾:lambda表达式是一种极简的表达艺术,但仅用于函数式接口new Thread(()->{for (int i = 0; i < 40; i++) {ticket.sale();}},"A").start();new Thread(()->{for (int i = 0; i < 40; i++) {ticket.sale();}},"B").start();new Thread(()->{for (int i = 0; i < 40; i++) {ticket.sale();}},"C").start();}
}// 高耦合,对象及为对象,不要附加多余功能,不要将其变成线程类
class Ticket{// 属性、方法private int ticketNums = 30;// synchronized 本质就是锁,队列public synchronized void sale(){if (ticketNums > 0) {System.out.println(Thread.currentThread().getName()+" sales "+ticketNums--+",and remains"+ticketNums);}}
}

Lock锁实现线程安全示例

java.util.concurrent.locks下有三个接口

  • Condition
  • Lock
  • ReadWriteLock

Lock接口

在这里插入图片描述

实现类:

  • ReentranLock(可重入锁)
  • ReentrantReadWriteLock.ReadLock(读锁)
  • ReentrantReadWriteLock.WriteLock(写锁)

在ReentrantLock中,其构造函数:

在这里插入图片描述

公平锁:先到先得

非公平锁(默认):可以插队,看CPU调度

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockDemo02 {public static void main(String[] args) {// 声明一个票对象,使3个线程可以调用买票方法Ticket ticket = new Ticket();// 使用lambda表达式,回顾:lambda表达式是一种极简的表达艺术,但仅用于函数式接口new Thread(()->{for (int i = 0; i < 40; i++) ticket.sale();},"A").start();new Thread(()->{for (int i = 0; i < 40; i++) ticket.sale();},"B").start();new Thread(()->{for (int i = 0; i < 40; i++) ticket.sale();},"C").start();}
}// 高耦合,对象及为对象,不要附加多余功能,不要将其变成线程类
// 使用lock锁
/*
Lock三部曲
1、 new ReentrantLock()
2、 lock.lock()
3、 finally => lock.unlock()*/
class Ticket{// 属性、方法private int ticketNums = 30;Lock lock = new ReentrantLock();// synchronized 本质就是锁,队列public  void sale(){lock.lock();// 加锁try {//业务代码if (ticketNums > 0) {System.out.println(Thread.currentThread().getName()+" sales "+ticketNums--+",and remains"+ticketNums);}} catch (Exception e) {throw new RuntimeException(e);} finally {lock.unlock();// 解锁}}
}

Lock与synchronized的区别

1、synchronized内置的Java关键字;而Lock是一个Java类

2、synchronized无法判断获取锁的状态;Lock可以判断是否获取到了锁

3、synchronized会自动释放锁;Lock必须手动释放锁,如果不释放锁,则会造成死锁

4、synchronized线程1(获得锁),线程2(一直等待); Lock锁时,线程2就不一定会等待下去

5、synchronized可重入锁,不可中断的,非公平锁(不可更改);Lock,可重入锁,可以判断锁,默认非公平(可以修改)

6、synchronized适合锁少量的代码同步问题;Lock锁适合锁大量的同步代码

锁是什么,如何判断锁的是谁

看8锁现象!!!

生产者和消费者问题

生产者消费者问题 synchronized版

package PC;/*** 线程之间的通信问题:生产者和消费者问题   等待唤醒  通知唤醒* 线程交替执行 A   B 操作同一个变量 num = 0* A num+1* B num-1*/
public class A {public static void main(String[] args) {Data data = new Data();new Thread(()->{for (int i = 0; i < 10; i++) {try {data.increment();} catch (InterruptedException e) {throw new RuntimeException(e);}}},"A").start();new Thread(()->{for (int i = 0; i < 10; i++) {try {data.decrement();} catch (InterruptedException e) {throw new RuntimeException(e);}}},"B").start();}
}// 口诀:等待,业务,通知
// 资源类
class Data{private int number = 0;// +1操作public synchronized void increment() throws InterruptedException {// 等待if (number != 0){this.wait();}// 业务number++;System.out.println(Thread.currentThread().getName()+"=>"+number);// 通知其他线程,我加一完毕this.notifyAll();}// -1操作public synchronized void decrement() throws InterruptedException {// 等待if (number == 0){this.wait();}// 业务number--;System.out.println(Thread.currentThread().getName()+"=>"+number);// 通知其他线程,我减一完毕this.notifyAll();}
}

这真的线程安全吗?如果有四个线程同时跑呢?

答:不安全,当四个线程在跑时,则会出现意料之外的情况

在这里插入图片描述

为什么会产生这种情况?

答:造成这种现象的原因是虚假唤醒

什么是虚假唤醒?

多线程环境下,有多个线程执行了wait()方法,需要其他线程执行notify()或者notifyAll()方法去唤醒它们,假如多个线程都被唤醒了,但是只有其中一部分是有用的唤醒操作,其余的唤醒都是无用功;对于不应该被唤醒的线程而言,便是虚假唤醒。
比如:仓库有货了才能出库,突然仓库入库了一个货品;这时所有的线程(货车)都被唤醒,来执行出库操作;实际上只有一个线程(货车)能执行出库操作,其他线程都是虚假唤醒。
参考博客:Java线程虚假唤醒是什么、如何避免?_java 虚假唤醒_秃秃爱健身的博客-CSDN博客

很重要:以下是个人感悟!

其实当初笔者在此处还是很困惑的,为什么虚假唤醒会造成线程同时运行而不顾if条件语句,后来笔者意识到一个很重要的问题:“wait()方法会使线程放弃锁”。也就是说,当A线程拿到了同步锁之后,进入if条件语句判断,如果此时条件为true,它会进入waiting状态并放弃同步锁,因此,C线程在这段时间有可能会乘虚而入,抢在B线程或D线程将A线程唤醒前进入同步代码块,同样进入if语句的waiting状态,之后,B线程或D线程完成其业务逻辑后,执行notifyAll()方法,就会将A线程与C线程同时唤醒,然后两者都会执行业务逻辑,导致一次减,两次加,与我们的预期(我们的逻辑是加一次,减一次)不符。因此线程不安全

以上解释只是个人猜想,还未曾验证过,比如将wait换成sleep,抱着线程休眠是否会出现同样的问题

如何避免虚假唤醒?

将if条件语句改为while循环语句

当使用if条件语句时,如果线程在if条件语句中被wait中断退出,当其重新进入回到它原本所在的位置后就会发现,它已经进行过判断了,接下来,就算已经有线程抢先一步操作,它也会义无反顾地往下走,因为没有条件能够拦住它啦!

而当我们使用while循环语句会发现:(以下是官方文档所给的推荐代码)

synchronized (obj) { while (<condition does not hold> and <timeout not exceeded>) {  long timeoutMillis = ... ; // recompute timeout values int nanos = ... ; obj.wait(timeoutMillis, nanos); } ... // Perform action appropriate to condition or timeout 
}  

如果线程在while循环中被wait中断退出,当其重新进入回到它原本所在的位置后就会发现,本次循环已经结束,**接下来并不是执行后面的业务代码,而是返回到while循环开头,重新判断一次是否满足条件。**这样的操作就保证了即使在退出重进,也会进行再一次的判断确保线程安全。

以下为示例中被修改的代码片段:

public synchronized void increment() throws InterruptedException {// 等待// ***************** 此处的if被改为while **********************while (number != 0){this.wait();}// 业务number++;System.out.println(Thread.currentThread().getName()+"=>"+number);// 通知其他线程,我加一完毕this.notifyAll();
}// -1操作
public synchronized void decrement() throws InterruptedException {// 等待// ***************** 此处的if被改为while **********************while (number == 0){this.wait();}// 业务number--;System.out.println(Thread.currentThread().getName()+"=>"+number);// 通知其他线程,我减一完毕this.notifyAll();
}

生产者消费者问题 JUC版

对应于synchronized,JUC版本下,Lock锁也有对应的唤醒与停止方法,分别是condition接口下的signal()与await()

以下是官方文档的描述:

在这里插入图片描述

示例代码如下:

package PC;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class B {public static void main(String[] args) {Data2 data = new Data2();new Thread(()->{for (int i = 0; i < 10; i++) {try {data.increment();} catch (InterruptedException e) {throw new RuntimeException(e);}}},"A").start();new Thread(()->{for (int i = 0; i < 10; i++) {try {data.decrement();} catch (InterruptedException e) {throw new RuntimeException(e);}}},"B").start();new Thread(()->{for (int i = 0; i < 10; i++) {try {data.increment();} catch (InterruptedException e) {throw new RuntimeException(e);}}},"C").start();new Thread(()->{for (int i = 0; i < 10; i++) {try {data.decrement();} catch (InterruptedException e) {throw new RuntimeException(e);}}},"D").start();}
}// 口诀:等待,业务,通知
// 资源类
class Data2{private int number = 0;Lock lock = new ReentrantLock();// +1操作public void increment() throws InterruptedException {Condition condition = lock.newCondition();/*** condition.await(); 等待* condition.signalAll(); 唤醒全部*/try {lock.lock();// =============== 业务代码 ==================// 等待while (number != 0){condition.await();}// 业务number++;// 通知其他线程,我加一完毕System.out.println(Thread.currentThread().getName()+"=>"+number);// ===========================================} catch (Exception e) {throw new RuntimeException(e);}finally{lock.unlock();}}// -1操作public void decrement() throws InterruptedException {Condition condition = lock.newCondition();try {lock.lock();// =============== 业务代码 ==================// 等待while (number == 0){}// 业务number--;// 通知其他线程,我减一完毕System.out.println(Thread.currentThread().getName()+"=>"+number);// ===========================================} catch (Exception e) {throw new RuntimeException(e);  } finally {lock.unlock();}}
}

Condition 的优势在哪里

可以实现精准的通知和唤醒线程

以下示例实现精准唤醒线程,在A线程执行完后精准唤醒B线程执行,B线程执行完后精准唤醒C线程执行,C线程执行完后精准唤醒A线程执行

package PC;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** A 执行完调用 B,B 执行完调用 C,C 执行调用 A*/
public class C {public static void main(String[] args) {// new 资源类Data3 data = new Data3();// 创建线程并执行线程new Thread(()->{for (int i = 0; i < 5; i++) data.printA();},"A").start();new Thread(()->{for (int i = 0; i < 5; i++) data.printB();},"B").start();new Thread(()->{for (int i = 0; i < 5; i++) data.printC();},"C").start();}
}//资源类
class Data3{private Lock lock = new ReentrantLock();private Condition conditionA = lock.newCondition();private Condition conditionB = lock.newCondition();private Condition conditionC = lock.newCondition();private int number = 1; // 若number=1则A执行,若number=2则B执行,若number=3则C执行public void printA(){lock.lock();try {// 业务, 判断->执行->通知while(number != 1){conditionA.await();}System.out.println(Thread.currentThread().getName()+" now is AAAAA time!");// 唤醒指定的线程Bnumber ++;conditionB.signal();} catch (Exception e) {throw new RuntimeException(e);} finally {lock.unlock();}}public void printB(){lock.lock();try {while(number != 2){conditionB.await();}System.out.println(Thread.currentThread().getName()+"now is BBBBB time!");// 唤醒指定线程Cnumber++;conditionC.signal();} catch (Exception e) {throw new RuntimeException(e);} finally {lock.unlock();}}public void printC(){lock.lock();try {while(number != 3){conditionC.await();}System.out.println(Thread.currentThread().getName()+"now is CCCCC time!");number = 1;conditionA.signal();} catch (Exception e) {throw new RuntimeException(e);} finally {lock.unlock();}}}

总结:

  • Lock锁与synchronized锁的区别要分清
  • 在多线程情况下,不要使用if条件语句来判断是否wait或await某一线程,要用while循环语句来判断,否则线程不安全
  • Lock锁下,使用await与signal等价于wait和notify,但有所区别,Lock锁更加灵活和可以精准唤醒某些线程

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

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

相关文章

ElasticSearch-数据查询

ElasticSearch-数据查询 目录概述需求&#xff1a; 设计思路实现思路分析1.查询某索引下的所有数据2.二、条件查询3.、条件查询方式二4.四、分页查询5.五、格式化数据 六、排序七 其他条件执行的成立的查询九、范围查询 参考资料和推荐阅读 Survive by day and develop by nigh…

基于类电磁机制优化的BP神经网络(分类应用) - 附代码

基于类电磁机制优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于类电磁机制优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.类电磁机制优化BP神经网络3.1 BP神经网络参数设置3.2 类电磁机制算法应用 4…

使用Premiere、PhotoShop和Audition做视频特效

今天接到一个做视频的任务&#xff0c;给一个精忠报国的视频&#xff0c;要求&#xff1a;   ①去掉人声&#xff0c;就是将唱歌的人声去掉&#xff0c;只留下伴奏&#xff1b;   ②截图视频中的横幅&#xff0c;做一个展开的效果&#xff0c;类似卷纸慢慢展开&#xff1b;…

使用 Tkinter Canvas 小部件添加放大镜功能?

一、说明 据我所知&#xff0c;内置的 Tkinter Canvas 类比例不会自动缩放图像。如果您无法使用自定义小部件&#xff0c;则可以缩放原始图像并在调用缩放函数时将其替换在画布上。 二、实现图像放大镜技术细节 我如何将放大和缩小添加到以下脚本中&#xff0c;我想将其绑定到…

【狂神说】HTML详解

目录 1 HTML概述1.1 什么是HTML1.2 HTML发展史1.3 HTML5的优势1.4 W3C标准 2 网页2.1 网页基本信息2.2 网页基本标签2.2.1 标题标签2.2.2 段落标签2.2.3 换行标签2.2.4 水平线标签2.2.5 字体样式标签&#xff1a;粗体、斜体2.2.6 注释和特殊符号 2.3 图像标签2.4 链接标签邮箱链…

高质量床上用品类网站带手机端的pbootcms模板

模板介绍&#xff1a; 这是一个基于PbootCMS内核开发的床上用品类网站模板&#xff0c;专为床上用品、家用纺织类企业设计和开发。它不仅提供了网站界面简洁简单、易于管理的特点&#xff0c;还附带了测试数据&#xff0c;方便用户进行演示和学习。 模板特点&#xff1a; 采用…

【TES720D-KIT】青翼自研基于复旦微FMQL20S400全国产化ARM开发套件(核心板+底板)

TES720D-KIT是专门针对我司TES720D&#xff08;基于复旦微FMQL20S400的全国产化ARM核心板&#xff09;的一套开发套件&#xff0c;它包含1个TES720D核心板&#xff0c;加上一个TES720D-EXT扩展底板。 FMQL20S400是复旦微电子研制的全可编程融合芯片&#xff0c;在单芯片内集成…

HTTP 原理与CND原理

1 HTTP 原理 HTTP是一个无状态的协议。无状态是指客户机&#xff08;Web浏览器&#xff09;和服务器之间不需要建立持久的连接&#xff0c;这意味着当一个客户端向服务器端发出请求&#xff0c;然后服务器返回响应(response)&#xff0c;连接就被关闭了&#xff0c;在服务器端…

与HTTP相关的各种协议

TCP/IP TCP/IP协议是目前网络世界“事实上”的标准通信协议&#xff0c;实际上是一系列网络通信协议的统称&#xff0c;其中最核心的两个协议是 TCP和IP&#xff0c;其他的还有 UDP、ICMP、ARP 等等&#xff0c;共同构成了一个复杂但有层次的协议栈。 这个协议栈有四层&#x…

前端需要了解的浏览器缓存知识

文章目录 前言为什么需要缓存&#xff1f;DNS缓存缓存读写顺序缓存位置memory cache&#xff08;浏览器本地缓存&#xff09;disk cache&#xff08;硬盘缓存&#xff09;重点&#xff01;&#xff01;&#xff01; 缓存策略 - 强缓存和协商缓存1&#xff09;强缓存ExpiresCach…

城市生命线专题周丨宏电燃气管线智慧化运营解决方案,助力燃气安全运营高质量发展

方案背景 随着我国城市发展建设速度的加快和国家能源结构的调整&#xff0c;天燃气走进了千家万户&#xff0c;燃气门站和城市燃气管网规模越来越庞大。此外&#xff0c;近年燃气泄漏导致的大型爆炸事件频发&#xff0c;给人民的生命安全和财产安全带来灾难性伤害。 行业痛点 …

Android---Android 是如何通过 Activity 进行交互的

相信对于 Android 工程师来说&#xff0c;startActivity 就像初恋一般。要求低&#xff0c;见效快&#xff0c;是每一个菜鸟 Android 工程师迈向高级 Android 工程师的必经阶段。经过这么多年的发展&#xff0c;startActivity 在 google 的调教下已经变得愈发成熟&#xff0c;对…

浅析ArkTS的起源和演进

1 引言 Mozilla创造了JS&#xff0c;Microsoft创建了TS&#xff0c;Huawei进一步推出了ArkTS。 从最初的基础的逻辑交互能力&#xff0c;到具备类型系统的高效工程开发能力&#xff0c;再到融合声明式UI、多维状态管理等丰富的应用开发能力&#xff0c;共同组成了相关的演进脉…

JimuReport 积木报表 v1.6.4 稳定版本正式发布 — 开源免费的低代码报表

项目介绍 一款免费的数据可视化报表&#xff0c;含报表和大屏设计&#xff0c;像搭建积木一样在线设计报表&#xff01;功能涵盖&#xff0c;数据报表、打印设计、图表报表、大屏设计等&#xff01; Web 版报表设计器&#xff0c;类似于excel操作风格&#xff0c;通过拖拽完成报…

内容分发网络CDN分布式部署真的可以加速吗?原理是什么?

Cdn快不快&#xff1f;她为什么会快&#xff1f;同样的带宽为什么她会快&#xff1f;原理究竟是什么&#xff0c;同学们本着普及知识的想法&#xff0c;我了解的不是很深入&#xff0c;适合小白来看我的帖子&#xff0c;如果您是大佬还请您指正错误的地方&#xff0c;先谢谢大佬…

[部署网站]01安装宝塔面板搭建WordPress

宝塔面板安装WordPress&#xff08;超详细&#xff09;_Wordpress主题网 参考教程 宝塔面板 - 简单好用的Linux/Windows服务器运维管理面板 官网 1.首先你需要一个服务器或者主机 &#xff08;Windows系统或者Linux系统都可以&#xff09; 推荐Linux系统更稳定&#xff0c;…

设计模式:简单工厂模式(C#、JAVA、JavaScript、C++、Python、Go、PHP):

简介&#xff1a; 简单工厂模式&#xff0c;它提供了一个用于创建对象的接口&#xff0c;但具体创建的对象类型可以在运行时决定。这种模式通常用于创建具有共同接口的对象&#xff0c;并且可以根据客户端代码中的参数或配置来选择要创建的具体对象类型。 在简单工厂模式中&am…

SaaS系统用户权限设计

SaaS系统用户权限设计 学习目标&#xff1a; 理解RBAC模型的基本概念及设计思路 了解SAAS-HRM中权限控制的需求及表结构分析完成组织机构的基本CRUD操作 完成用户管理的基本CRUD操作完成角色管理的基本CRUD操作 组织机构管理 需求分析 需求分析 实现企业组织结构管理&#xff0…

期中考Web复现

第一题 1z_php <?php //Yeedo told you to study hard! echo !(!(!(!(include "flag.php")||(!error_reporting(0))||!isset($_GET[OoO])||!isset($_GET[0o0])||($_GET[OoO]2023)||!(intval($_GET[OoO][0])2023)||$_GET[0o0]$_GET[OoO]||!(md5($_GET[0o0])md5($_…

VM虚拟机运行的Ubuntu连入同一局域网,并实现双机方法

环境&#xff1a; Windows 10 VMware Workstation Pro 16 Ubuntu 20.4 在虚拟机设置桥接模式 确保虚拟机处于关闭状态&#xff0c;在Vm中设置&#xff1a; 编辑->虚拟网络编辑器 如果你以前设置过&#xff0c;可以重置之。 重置之后&#xff0c;添加桥接模式&#xff1a; …