1501 - JUC高并发

须知少许凌云志,曾许人间第一流

看的是尚硅谷的视频做的学习总结,感恩老师,下面是视频的地址

传送门icon-default.png?t=N7T8https://www.bilibili.com/video/BV1Kw411Z7dF

0.思维导图

1.JUC简介

1.1 什么是JUC

JUC, java.util.concurrent工具包的简称,一个处理线程的工具包。

1.2 进程和线程的概念

1.2.1 进程与线程

  • 进程

指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程是资源分配的最小单位。

  • 线程

系统分配处理器时间资源的基本单元;进程之内独立执行的一个单元执行流;线程是程序执行的最小单位。

一个进程包含多个线程。

1.2.2 线程的状态

查看jdk源码

1.2.3 wait 和 sleep

区别

  1. sleep是Thread的静态方法;wait是Object的方法,任何对象实例都能调用。
  2. sleep不会释放锁,它也不需要占用锁;wait会释放锁。(wait会释放锁去睡觉,sleep会抓住锁去睡觉,在哪里谁就会在哪里醒)
  3. 它们都可以被interrupt方法中断。

1.2.4 并发和并行

  • 并发

同一时间间隔内多个线程交替执行,实际上是宏观上并行,微观上串行。(春运抢票,电商秒杀,抢同一个资源

  • 并行

同一时刻多个线程正在执行,多核并行。(一边看书,一边听音乐)

1.2.5 管程

叫 Monitor 监视器,就是锁。

是一种同步机制,保证同一个时间,只有一个线程访问被保护的数据或者代码。

1.2.6 用户线程和守护线程

  • 用户线程

自定义的线程,不随主线程的结束而结束。主线程结束了,用户线程还会运行,jvm还是存活状态。

  • 守护线程

随着主线程的结束而结束,如垃圾回收线程。主线程结束,jvm结束。

2.Lock接口

2.1 Synchronized

2.1.1 Synchronized作用范围

synchronized是Java的关键字,是一种同步锁。

synchronized的作用范围可以根据使用方式的不同而有所区别,主要有以下几种情况:

  • 同步方法(实例方法)
public class SynchronizedExample {
    public synchronized void syncMethod() {
        // 同步代码块
    }
}

synchronized修饰一个实例方法时,它作用于整个方法体。当一个线程进入一个对象的同步方法时,其他线程在该对象上调用同步方法时会被阻塞,直到第一个线程退出该方法。这种同步方式是基于对象的,也就是说,不同的对象实例的同步方法是互不干扰的。

  • 同步静态方法
public class SynchronizedExample {
    public static synchronized void syncStaticMethod() {
        // 同步代码块
    }
}

synchronized修饰一个静态方法时,它作用于整个静态方法体。由于静态方法是属于类的,而不是类的实例,因此这种同步是基于类的。当一个线程进入一个类的同步静态方法时,其他线程在该类上调用同步静态方法时会被阻塞。

  • 同步代码块
public class SynchronizedExample {
    private final Object lock = new Object();

    public void someMethod() {
        synchronized (lock) {
            // 同步代码块
        }
    }
}

synchronized也可以用来修饰一个代码块,此时需要指定一个对象作为锁对象。当线程进入同步代码块时,它会获取指定对象的锁,如果其他线程已经持有该对象的锁,则进入阻塞状态。这种同步方式允许更细粒度的控制,只同步需要同步的代码部分。 

2.1.2 多线程编程步骤

第一步:创建资源类,在资源类创建属性和操作方法。

第二步:创建多个线程,去调用资源类的操作方法。

2.1.3 Synchronized实现买票示例

需求:3个售票员卖一百张门票。

分析:资源是一百张门票,操作方法是买票,创建多个线程是3个售货员。

代码示例

// 第一步:定义资源类
class Ticket {
    // 第二步:定义资源
    public int number = 100;

    // 第三步:定义操作方法
    public synchronized void sale() {
        if (number > 0) {
            System.out.println(Thread.currentThread().getName() + "卖出第" + (100 - number + 1) + "张票,剩余" + --number + "张票");
        }
    }
}

public class SaleTicket {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        // 使用匿名内部类创建线程
        Runnable runnable = () -> {
            for (int i = 0; i < 150; i++) {
                ticket.sale();
            }
        };

        // 创建多个线程进行卖票
        new Thread(runnable,"售票员1").start();
        new Thread(runnable,"售票员2").start();
        new Thread(runnable,"售票员3").start();
    }
}

输出结果

2.2 Lock

2.2.1 Lock接口的介绍

Lock 实现提供比使用 synchronized 方法和语句可以获得的更广泛的锁定操作。

2.2.2 使用Lock实现卖票例子

// 第一步:定义资源类
class Ticket {
    // 第二步:定义资源
    public int number = 100;

    // 创建可重入锁
    private final ReentrantLock lock = new ReentrantLock();

    // 第三步:定义操作方法
    public synchronized void sale() {
        // 手动上锁
        lock.lock();
        try{
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出第" + (100 - number + 1) + "张票,剩余" + --number + "张票");
            }
        }finally {
            // 手动解锁
            lock.unlock();
        }
    }
}

public class SaleTicket {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        // 使用匿名内部类创建线程
        Runnable runnable = () -> {
            for (int i = 0; i < 150; i++) {
                ticket.sale();
            }
        };

        // 创建多个线程进行卖票
        new Thread(runnable,"售票员1").start();
        new Thread(runnable,"售票员2").start();
        new Thread(runnable,"售票员3").start();
    }
}

2.2.3 synchronized和Lock两者差异

  • synchronized是java内置关键字。Lock不是内置,可以实现同步访问且比 synchronized中的方法更加丰富。
  • synchronized自动释放锁,而lock需手动释放锁(不解锁会出现死锁,需要在 finally 块中释放锁)。
  • Lock 可以让等待锁的线程响应中断,而等待synchronized锁的线程不能响应中断,会一直等待。
  • 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。
  • Lock 可以提高多个线程进行读操作的效率(当多个线程竞争的时候,Lock 性能远远好于synchronized)。

2.2.4 创建线程的四种方式

  1. 继承Thread类
  2. 实现Runnable接口
  3. 使用Callable接口
  4. 使用线程池

3.线程间通信

3.1 多线程编程步骤

第一步:创建资源类,在资源类创建属性和操作方法。

第二步:在资源类操作方法,判断、干活、通知。

第三步:创建多个线程,去调用资源类的操作方法。

第四步:防止虚假唤醒。

3.2 synchronized 实现线程通信案例

关键字 synchronized 与 wait()/notify() 这两个方法一起使用可以实现等待/通知模式。

代码示例

// 第一步:创建资源类,定义属性和操作方法
class Share {
    private int number = 0;

    // +1的方法
    public synchronized void incr() throws InterruptedException {
        // 第二步:判断
        if (number != 0) {
           this.wait();
        }
        // 干活
        number++;
        System.out.println(Thread.currentThread().getName() + ":" + number);
        // 通知其他线程
        this.notifyAll();
    }

    // -1的方法
    public synchronized void decr() throws InterruptedException {
        // 判断
        if (number != 1) {
            this.wait();
        }
        // 干活
        number--;
        System.out.println(Thread.currentThread().getName() + ":" + number);
        // 通知其他线程
        this.notifyAll();
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        // 第三步:创建多个线程,调用资源类中的操作方法
        Share share = new Share();

        new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "生产").start();

        new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "消费").start();

    }

}

3.3 虚假唤醒问题(if改while)

当多个线程都处于等待集合中,一旦收到通知,可以直接操作而不再判断,这叫做虚假唤醒问题。 将this.wait()放在while循环中可以解决该问题。

代码示例

class Share {
    int number = 0;

    public synchronized void incr() throws InterruptedException {
        //判断
        if (number != 0) {
            this.wait();//这里会释放锁
        }
        //执行
        number++;
        System.out.print(Thread.currentThread().getName() + " : " + number + "-->");
        // 通知
        this.notifyAll();
    }

    public synchronized void decr() throws InterruptedException {
        //判断
        if (number != 1) {
            this.wait();
        }
        //执行
        number--;
        System.out.println(Thread.currentThread().getName() + " : " + number);
        //通知
        this.notifyAll();
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        Share share = new Share();
        new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "C").start();

        new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "D").start();
    }
}

输出结果

原因

由于 wait() 方法使线程在哪里睡就在哪里醒,B和D在wait后被唤醒,执行操作时不会再通过 if 判断,从而导致出现异常结果。
为了保证线程“醒了”之后再次判断,需要将wait() 方法放入while循环中。 

class Share {
    int number = 0;

    public synchronized void incr() throws InterruptedException {
        //判断
        while (number != 0) {
            this.wait();
        }
        //执行
        number++;
        System.out.print(Thread.currentThread().getName() + " : " + number + "-->");
        // 通知
        this.notifyAll();
    }

    public synchronized void decr() throws InterruptedException {
        //判断
        while (number != 1) {
            this.wait();
        }
        //执行
        number--;
        System.out.println(Thread.currentThread().getName() + " : " + number);
        //通知
        this.notifyAll();
    }
}

3.4 Lock实现线程间通信案例

在 Lock 接口中,有一个 newCondition() 方法,返回一个新 Condition 绑定到该实例 Lock 实例。

Condition 类中有 await() signalAll() 等方法,和 synchronized 实现案例中的 wait() 和 notifyAll() 方法相同。

代码示例

class Share {
    private int number = 0;
    //创建Lock
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void incr() throws InterruptedException {
        lock.lock();
        try {
            while (number != 0) {
                condition.await();
            }
            number++;
            System.out.print(Thread.currentThread().getName() + " : " + number + "-->");
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public void decr() throws InterruptedException {
        lock.lock();
        try {
            //判断
            while (number != 1) {
                condition.await();
            }
            //执行
            number--;
            System.out.println(Thread.currentThread().getName() + " : " + number);
            //通知
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        Share share = new Share();
        new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "C").start();

        new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "D").start();
    }
}

4.线程间定制化通信

4.1 Condition 类选择性通知

案例: 启动三个线程,按照如下要求执行,AA打印5此,BB打印10次,CC打印15次,一共进行10轮
具体思路: 每个线程添加一个标志位,是该标志位则执行操作,并且修改为下一个标志位,通知下一个标志位的线程。

代码示例

class ShareResource {
    // 标志位 1:AA 2:BB 3:CC
    private int flag = 1;
    private ReentrantLock lock = new ReentrantLock();
    // 创建三个Condition对象,实现定向唤醒
    Condition c1 = lock.newCondition();
    Condition c2 = lock.newCondition();
    Condition c3 = lock.newCondition();

    public void print5(int loop) throws InterruptedException {
        lock.lock();
        try {
            while (flag != 1) {
                c1.await();
            }
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + " :: " + i + ", loop=" + loop);
            }
            flag = 2;
            c2.signal();
        } finally {
            lock.unlock();
        }
    }

    public void print10(int loop) throws InterruptedException {
        lock.lock();
        try {
            while (flag != 2) {
                c2.await();
            }
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + " :: " + i + ", loop=" + loop);
            }
            flag = 3;
            c3.signal();
        } finally {
            lock.unlock();
        }
    }

    public void print15(int loop) throws InterruptedException {
        lock.lock();
        try {
            while (flag != 3) {
                c3.await();
            }
            for (int i = 0; i < 15; i++) {
                System.out.println(Thread.currentThread().getName() + " :: " + i + ", loop=" + loop);
            }
            flag = 1;
            c1.signal();
        } finally {
            lock.unlock();
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        ShareResource shareResource = new ShareResource();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    shareResource.print5(i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "AA").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    shareResource.print10(i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "BB").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    shareResource.print15(i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "CC").start();
    }
}

上面的代码采用单标志法,设置一个公用整型变量flag,用于指示被允许进入临界区的进程编号。

若 flag =1,则允许 AA 进程进入临界区;

若 flag =2,则允许 BB 进程进入临界区;

若 flag =3,则允许 CC 进程进入临界区。

该算法可确保每次只允许一个进程进入临界区。但两个进程必须交替进入临界区,若某个进程不再进入临界区,则另一个进程也无法进入临界区。在线程的run()方法调用中设置不同的loop次数,在后期会有部分线程不能访问 Share 资源了,违背了"空闲让进"原则,让资源利用不充分。

4.2 进程/线程同步四个原则

  • 空闲让进:临界区空闲时,可以允许一个请求进入临界区的进程立即进入临界区。
  • 忙则等待:当已经有进程进入临界区的时候,其他试图进入临界区的进程必须等待。
  • 有限等待:对请求访问的进程,应保证能在有限时间内进入临界区。
  • 让权等待:当进程不能进入临界区的时候,应立即释放处理机,防止进程忙等待。

5.集合的线程安全

集合线程不安全,简单来说就是底层的方法没有使用同步安全锁。

5.1 ArrayList 不安全

jdk源码

没有使用synchronized关键字,所以在多线程并发时,会出现线程安全问题。

代码示例

public class ThreadDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

运行报错

5.2 解决方案 Vector

jdk源码

Vector类中的方法加了synchronized关键字,因此可以保证线程安全。

代码改造

public class ThreadDemo {
    public static void main(String[] args) {
        List<String> list = new Vector<>();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

 运行结果

5.3 解决方案 synchronizedList(List list) 

Collections 接口中的 synchronizedList(List list) 方法,可以将传入的 List列表对象转为同步(线程安全的)列表并返回。

语法

List<String> list = Collections.synchronizedList(new ArrayList<>());

jdk源码

代码示例

public class ThreadDemo {
    public static void main(String[] args) {
        List<String> list = Collections.synchronizedList(new ArrayList<>());
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

5.4 解决方案 CopyOnWriteArrayList

语法

List<String> list = new CopyOnWriteArrayList<>();

CopyOnWriteArrayList 采用读写分离的思想,读操作不加锁,写操作加锁。(redis也使用)

  • 读的时候并发读取旧数据(多个线程操作)
  • 写的时候独立,先复制一份比旧数据长 1 的数据出来,在最后添加数据,旧新合并,完成写操作,之后就可以读所有数据(每次加新内容都写到新区域,合并之前旧区域,读取新区域添加的内容)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

433/315无线门铃解决方案,功耗超低,通信距离可达200米

无线门铃是一种常见的智能电子设备&#xff0c;提供了方便&#xff0c;安全的门铃解决方案&#xff0c;适用于各种住宅和商业环境。芯岭技术的无线门铃方案可适用于普通的433/315无线门铃应用&#xff0c;支持不同的语音选择&#xff0c;支持交流和直流应用&#xff0c;支持不同…

Python——泰坦尼克号数据分析

目录 🧾1.数据集(部分数据) ✏️ 2、导入数据集与必要模块 ⌨️ 3.数据预处理 1️⃣ isnull函数查看有无缺失值 2️⃣fillna函数填充缺失值 📍 Age字段使用平均值填充缺失值 📍 Embarked字段填充缺失值 3️⃣ 删除缺失值较多的字段 📊 4.数据可视化 1️⃣ di…

每日一练 2024.6.7

给你一个仅由 大写 英文字符组成的字符串 s 。 你可以对此字符串执行一些操作&#xff0c;在每一步操作中&#xff0c;你可以从 s 中删除 任一个 "AB" 或 "CD" 子字符串。 通过执行操作&#xff0c;删除所有 "AB" 和 "CD" 子串&#x…

【面试八股总结】死锁:产生条件、预防死锁、处理死锁、避免死锁

一、什么是死锁&#xff1f; 死锁是指两个&#xff08;或多个&#xff09;线程互相等待对方数据的过程&#xff0c;死锁的产生导致程序卡死&#xff0c;不解锁程序将永远⽆法进⾏下 去 二、死锁产生条件 死锁只有同时满足以下四个条件才会发生&#xff1a;互斥条件&#xff1b…

笔记-2024视频会议软件技术选型方案

一、背景 视频会议系统是一种现代化的办公系统&#xff0c;它可以使不同会场的实时现场场景和语音互连起来&#xff0c;同时向与会者提供分享听觉和视觉的空间&#xff0c;使各与会方有“面对面”交谈的感觉。随着社会的发展&#xff0c;视频会议的应用越来越广泛&#xff0c;…

【数据分析基础】实验四 matplotlib数据可视化处理

一&#xff0e;实验目的 掌握扩展库matplotlib及其依赖库的安装。了解matplotlib的绘图一般过程。熟练掌握折线图、散点图、柱状图、饼状图、雷达图的绘制与常用属性的设置。掌握绘图区域的切分、绘制不同子图的方法。熟悉坐标轴、图像标题、图例等对象的属性设置操作。 二、实…

新品!和芯星通全系统全频高精度板卡UB9A0首发

6月6日&#xff0c;和芯星通发布了UB9A0全系统全频高精度GNSS板卡&#xff0c;主要应用于CORS站、便携基站、GNSS全球监测跟踪站等。延续了上一代产品高质量原始观测量的特点&#xff0c;UB9A0在性能和稳定性方面均表现出众。 UB9A0基于射频基带及高精度算法一体化的GNSS SoC芯…

Django 开发也在用 React!

你好&#xff0c;我是坚持分享干货的 EarlGrey&#xff0c;翻译出版过《Python编程无师自通》、《Python并行计算手册》等技术书籍。 如果我的分享对你有帮助&#xff0c;请关注我&#xff0c;一起向上进击。 在前天的推文里&#xff0c;我们分享了《Django 2024 年度报告》&am…

一起学大模型 - 一起动笔练习prompt的用法

文章目录 前言一、代码演示二、代码解析1. 导入所需的库和模块&#xff1a;2. 设置日志记录和初始化模型&#xff1a;3. 定义一个函数用于清理GPU内存&#xff1a;4. 定义一个继承自LLM基类的QianWenChatLLM类&#xff0c;并实现对话生成的逻辑&#xff1a;5. 示例代码的主体部…

柏曼护眼台灯值得入手吗?明基、书客实测对比

早期的台灯主要是以白炽灯为主&#xff0c;但随着LED技术的成熟&#xff0c;LED台灯逐渐成为主流。目前&#xff0c;台灯行业已经进入了一个高速发展的阶段&#xff0c;市场竞争也越来越激烈。如何选购护眼台灯也是大家最常问的问题&#xff0c;柏曼护眼台灯值得入手吗&#xf…

HTML静态网页成品作业(HTML+CSS)—— 电影泰坦尼克号介绍网页(4个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有4个页面。 二、作品演示 三、代…

2024年G3锅炉水处理证考试题库及G3锅炉水处理试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年G3锅炉水处理证考试题库及G3锅炉水处理试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#xff09;特种设备作业人员上岗证考试大纲随机…

咖啡机器人如何精准控制液位流量

在如今快节奏的生活中&#xff0c;精确控制液位流量的需求愈发迫切&#xff0c;特别是在咖啡机器人等精密设备中。为了满足这一需求&#xff0c;工程师们不断研发出各种先进的技术&#xff0c;以确保液体流量的精准控制。其中&#xff0c;霍尔式流量计和光电式流量计就是两种常…

如何用Postman做接口自动化测试?5个步骤带你轻松实现!

什么是自动化测试 把人对软件的测试行为转化为由机器执行测试行为的一种实践。例如GUI自动化测试&#xff0c;模拟人去操作软件界面&#xff0c;把人从简单重复的劳动中解放出来本质是用代码去测试另一段代码&#xff0c;属于一种软件开发工作&#xff0c;已经开发完成的用例还…

基于函数计算部署GPT-Sovits语音生成模型实现AI克隆声音

GPT-Sovits是一个热门的文本生成语音的大模型&#xff0c;只需要少量样本的声音数据源&#xff0c;就可以实现高度相似的仿真效果。通过函数计算部署GPT-Sovits模型&#xff0c;您无需关心GPU服务器维护和环境配置&#xff0c;即可快速部署和体验模型&#xff0c;同时&#xff…

基于ensp的园区网络搭建综合实验

核心技术介绍 1、虚拟局域网&#xff08;VLAN&#xff09; 2、链路聚合&#xff08;E-trunk&#xff09; 3、多生成树协议&#xff08;MSTP&#xff09; 4、VLANIF三层逻辑接口 5、虚拟路由冗余协议&#xff08;VRRP&#xff09; 6、开放式最短路径优先&#xff08;OSPF&…

【C++历练之路】C++11中的列表初始化声明新方法深入标准模板库的变革

W...Y的主页 &#x1f60a; 代码仓库分享&#x1f495; 目录 1. C11简介 2. 统一的列表初始化 2.1 &#xff5b;&#xff5d;初始化 2.2 std::initializer_list 3. 声明 3.1 auto 3.2 decltype 4.STL中一些变化 1. C11简介 在2003年C标准委员会曾经提交了一份技术勘误…

响应式流规范解析

在互联网应用构建过程中&#xff0c;我们知道可以采用异步非阻塞的编程模型来提高服务的响应能力。而为了实现异步非阻塞&#xff0c;我们可以引入数据流&#xff0c;并对数据的流量进行控制。我们来考虑一个场景&#xff0c;如果数据消费的速度跟不上数据发出的速度&#xff0…

腺苷调节合成高密度脂蛋白用于三阴性乳腺癌的化学免疫治疗

引用信息 文 章&#xff1a;Adenosine-modulating synthetic high-density lipoprotein for chemoimmunotherapy of triple-negative breast cancer 期 刊&#xff1a;Journal of Controlled Release&#xff08;影响因子&#xff1a;10.8&#xff09; 发表时间&am…

webgl_effects_stereo

ThreeJS 官方案例学习&#xff08;webgl_effects_stereo&#xff09; 1.效果图 2.源码 <template><div><div id"container"></div></div> </template> <script> import * as THREE from three; // 导入控制器 import { …