JUC高并发编程5:多线程锁

news2024/10/1 2:10:17

1 锁的八个问题演示

  1. 标准访问,先打印短信还是邮件
class Phone{

    public synchronized void sendSMS() throws InterruptedException {
        
        System.out.println("----------sendSMS");
    }

    public synchronized void sendEmail(){
        System.out.println("-----------sendEmail");
    }

    public void getHello(){
        System.out.println("-------------getHello");
    }
}

public class Lock_8 {

    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();

        new Thread(()->{
            try {
                phone.sendSMS();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        },"AA").start();

        Thread.sleep(100);

        new Thread(()->{
            try {
                phone.sendEmail();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        },"BB").start();
    }
}
  • 运行结果:
----------sendSMS
-----------sendEmail
  1. 停4秒在短信方法内,先打印短信还是邮件
class Phone{

    public synchronized void sendSMS() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("----------sendSMS");
    }

    public synchronized void sendEmail(){
        System.out.println("-----------sendEmail");
    }

    public void getHello(){
        System.out.println("-------------getHello");
    }
}

public class Lock_8 {

    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();

        new Thread(()->{
            try {
                phone.sendSMS();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        },"AA").start();

        Thread.sleep(100);

        new Thread(()->{
            try {
                phone.sendEmail();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        },"BB").start();
    }
}
  • 运行结果:
----------sendSMS
-----------sendEmail
  1. 新增普通的hello方法,是先打短信还是hello
class Phone{

    public synchronized void sendSMS() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("----------sendSMS");
    }

    public synchronized void sendEmail(){
        System.out.println("-----------sendEmail");
    }

    public void getHello(){
        System.out.println("-------------getHello");
    }
}

public class Lock_8 {

    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();

        new Thread(()->{
            try {
                phone.sendSMS();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        },"AA").start();

        Thread.sleep(100);

        new Thread(()->{
            try {
                phone.getHello();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        },"BB").start();
    }
}
  • 运行结果:
-------------getHello
----------sendSMS
  1. 现在有两部手机,先打印短信还是邮件
class Phone{

    public synchronized void sendSMS() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("----------sendSMS");
    }

    public synchronized void sendEmail(){
        System.out.println("-----------sendEmail");
    }

    public void getHello(){
        System.out.println("-------------getHello");
    }
}

public class Lock_8 {

    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
	    Phone phone2 = new Phone();
        new Thread(()->{
            try {
                phone.sendSMS();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        },"AA").start();

        Thread.sleep(100);

        new Thread(()->{
            try {
                phone2.sendEmail();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        },"BB").start();
    }
}
  • 运行结果:
-----------sendEmail
----------sendSMS

5.两个静态同步方法,1部手机,先打印短信还是邮件

class Phone{

    public static synchronized void sendSMS() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("----------sendSMS");
    }

    public static synchronized void sendEmail(){
        System.out.println("-----------sendEmail");
    }

    public void getHello(){
        System.out.println("-------------getHello");
    }
}

public class Lock_8 {

    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();

        new Thread(()->{
            try {
                phone.sendSMS();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        },"AA").start();

        Thread.sleep(100);

        new Thread(()->{
            try {
                phone.sendEmail();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        },"BB").start();
    }
}
  • 运行结果:
----------sendSMS
-----------sendEmail
  1. 两个静态同步方法,2部手机,先打印短信还是邮件
class Phone{

    public static synchronized void sendSMS() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("----------sendSMS");
    }

    public static synchronized void sendEmail(){
        System.out.println("-----------sendEmail");
    }

    public void getHello(){
        System.out.println("-------------getHello");
    }
}

public class Lock_8 {

    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
	    Phone phone2 = new Phone();
        new Thread(()->{
            try {
                phone.sendSMS();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        },"AA").start();

        Thread.sleep(100);

        new Thread(()->{
            try {
                phone2.sendEmail();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        },"BB").start();
    }
}
  • 运行结果:
----------sendSMS
-----------sendEmail
  1. 1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件
class Phone{

    public static synchronized void sendSMS() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("----------sendSMS");
    }

    public synchronized void sendEmail(){
        System.out.println("-----------sendEmail");
    }

    public void getHello(){
        System.out.println("-------------getHello");
    }
}

public class Lock_8 {

    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
	   
        new Thread(()->{
            try {
                phone.sendSMS();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        },"AA").start();

        Thread.sleep(100);

        new Thread(()->{
            try {
                phone.sendEmail();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        },"BB").start();
    }
}
  • 运行结果
-----------sendEmail
----------sendSMS
    1. 1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件
class Phone{

    public static synchronized void sendSMS() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("----------sendSMS");
    }

    public synchronized void sendEmail(){
        System.out.println("-----------sendEmail");
    }

    public void getHello(){
        System.out.println("-------------getHello");
    }
}

public class Lock_8 {

    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
	   	Phone phone2 = new Phone();
        new Thread(()->{
            try {
                phone.sendSMS();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        },"AA").start();

        Thread.sleep(100);

        new Thread(()->{
            try {
                phone2.sendEmail();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        },"BB").start();
    }
}
  • 运行结果:
-----------sendEmail
----------sendSMS
  • 结论
    synchronized实现同步的基础:Java中的每一个对象都可以作为锁。具体表现为以下3种形式。
    • 对于普通同步方法,锁是当前实例对象。
    • 对于静态同步方法,锁是当前类的class对象。
    • 对于同步方法块,锁是synchonized括号里配置的对象

2 公平锁与非公平锁

在多线程编程中,锁(Lock)是一种用于控制多个线程对共享资源访问的同步机制。锁可以分为公平锁(Fair Lock)和非公平锁(Non-Fair Lock)两种类型,它们在实现方式和行为上有显著的区别。

2.1 公平锁(Fair Lock)

2.1.1 概念

公平锁是指多个线程按照请求锁的顺序依次获得锁,即先请求的线程先获得锁。公平锁遵循FIFO(先进先出)原则,确保每个线程都有公平的机会获得锁。

2.1.2 特点

  • 公平性:确保每个线程按照请求锁的顺序获得锁,避免线程饥饿(Starvation)现象。
  • 性能较低:由于需要维护请求队列并按顺序分配锁,公平锁的性能通常比非公平锁低。
  • 适用场景:适用于需要严格保证线程执行顺序的场景,如某些实时系统或对公平性要求较高的应用。

2.1.3 Java中的实现

在Java中,ReentrantLock类可以通过构造函数参数指定是否使用公平锁。

ReentrantLock fairLock = new ReentrantLock(true); // 创建公平锁

2.2 非公平锁(Non-Fair Lock)

2.2.1 概念

非公平锁是指多个线程请求锁时,不保证按照请求的顺序获得锁。非公平锁允许线程插队,即一个新请求的线程可以抢占正在等待的线程的锁。

2.2.2 特点

  • 非公平性:不保证线程按照请求的顺序获得锁,允许线程插队。
  • 性能较高:由于不需要维护请求队列和按顺序分配锁,非公平锁的性能通常比公平锁高。
  • 适用场景:适用于对性能要求较高且对线程执行顺序要求不严格的场景,如大多数并发应用。

2.2.3 Java中的实现

在Java中,ReentrantLock类默认使用非公平锁。

ReentrantLock nonFairLock = new ReentrantLock(); // 创建非公平锁

ReentrantLock nonFairLock = new ReentrantLock(false); // 创建非公平锁

2.3 对比与选择

2.3.1 公平锁 vs 非公平锁

  • 公平性:公平锁保证线程按照请求顺序获得锁,非公平锁不保证。
  • 性能:公平锁性能较低,非公平锁性能较高。
  • 适用场景:公平锁适用于需要严格保证线程执行顺序的场景,非公平锁适用于对性能要求较高且对线程执行顺序要求不严格的场景。

2.3.2 选择原则

  • 性能优先:如果对性能要求较高,且对线程执行顺序要求不严格,选择非公平锁。
  • 公平性优先:如果需要严格保证线程执行顺序,避免线程饥饿现象,选择公平锁。

3 可重入锁

可重入锁是一种允许同一个线程多次获取同一个锁的同步机制。可重入锁的主要特点是,当一个线程已经持有某个锁时,它可以再次获取该锁而不会被阻塞。这种特性避免了死锁的发生,因为线程可以多次进入同一个锁保护的代码块

3.1 可重入锁的概念

可重入锁允许同一个线程多次获取同一个锁,而不会导致死锁。每次获取锁时,锁的计数器会增加,每次释放锁时,计数器会减少。只有当计数器归零时,锁才会被完全释放。

3.2 使用 synchronized 实现可重入锁

  • synchronized实现同步代码块
public class SyncLockDemo {

    public static void main(String[] args) {

        // synchronized
        Object o = new Object();

        new Thread(()->{
            synchronized (o){
                System.out.println(Thread.currentThread().getName() + " 外层");

                synchronized (o){
                    System.out.println(Thread.currentThread().getName() + " 中层");

                    synchronized (o){
                        System.out.println(Thread.currentThread().getName() + " 内层");
                    }
                }

            }
        },"t1").start();
    }
}

  • 运行结果:
t1 外层
t1 中层
t1 内层
  • synchronized实现同步方法
public class SyncLockDemo {

    public synchronized void add(){
        add();
    }
    public static void main(String[] args) {

        new SyncLockDemo().add();
    }
}

  • 运行结果:
    在这里插入图片描述

3.3 使用Lock实现可重入锁

  • Lock实现可重入锁
public class SyncLockDemo {
    public static void main(String[] args) {

        //Lock演示可重入锁
        Lock lock = new ReentrantLock();

        //创建线程
        new Thread(()->{
            try {
                // 上锁
                lock.lock();
                System.out.println(Thread.currentThread().getName() + " 外层");

                try {
                    // 上锁
                    lock.lock();
                    System.out.println(Thread.currentThread().getName() + " 内层");
                } finally {
                    // 解锁
                    lock.unlock();
                }
            } finally {
                // 解锁
                lock.unlock();
            }
        },"t1").start();


    }
}

  • 运行结果:
t1 外层
t1 内层
  • 注意:上锁lock.lock()和解锁lock.unlock()一定要成对出现,千万不要忘记解锁。

4 死锁

4.1 死锁的概念

两个或者两个以上进程在执行过程中,因为争夺资源而造成一种互相等待的现象,如果没有外力干涉,他们无法再执行下去
在这里插入图片描述

4.2 产生死锁的原因

  • 系统资源不足
  • 进程运行推进顺序不合适
  • 资源分配不当

4.3 验证是否死锁

在多线程编程中,死锁是一种常见的问题,它发生在两个或多个线程相互等待对方释放资源的情况下。为了验证是否发生了死锁,可以使用Java提供的工具 jpsjstack

4.3.1 jps 工具

jps 是Java Virtual Machine Process Status Tool的缩写,类似于Linux中的 ps -ef 命令,用于列出当前正在运行的Java进程。

  • 使用方法

在命令行中输入以下命令:

jps -l
  • 输出示例
47872
29048 com.yunyang.javacoder.sync.DeadLock
  • 4787229048 是Java进程的PID(进程ID)。
  • DeadLock 是Java进程的名称。

4.3.2 jstack 工具

jstack 是JVM自带的堆栈跟踪工具,用于生成Java虚拟机当前时刻的线程快照(thread dump)。通过分析线程快照,可以检查是否存在死锁。

  • 使用方法

在命令行中输入以下命令:

jstack <PID>

其中 <PID> 是Java进程的PID,可以通过 jps 命令获取。

  • 输出示例
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.152-b16 mixed mode):

"DestroyJavaVM" #24 prio=5 os_prio=0 tid=0x0000000002ee3800 nid=0x71c0 waiting
 on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"B" #23 prio=5 os_prio=0 tid=0x0000000021811800 nid=0x94f4 waiting for monitor
 entry [0x00000000220df000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.yunyang.javacoder.sync.DeadLock.lambda$main$1(DeadLock.java:41)

        - waiting to lock <0x000000076baa67e0> (a java.lang.Object)
        - locked <0x000000076baa67f0> (a java.lang.Object)
        at com.yunyang.javacoder.sync.DeadLock$$Lambda$2/1419810764.run(Unknow
n Source)
        at java.lang.Thread.run(Thread.java:748)

"A" #22 prio=5 os_prio=0 tid=0x0000000021836000 nid=0xd3d4 waiting for monitor
 entry [0x0000000021fdf000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.yunyang.javacoder.sync.DeadLock.lambda$main$0(DeadLock.java:27)

        - waiting to lock <0x000000076baa67f0> (a java.lang.Object)
        - locked <0x000000076baa67e0> (a java.lang.Object)
        at com.yunyang.javacoder.sync.DeadLock$$Lambda$1/913190639.run(Unknown
 Source)
        at java.lang.Thread.run(Thread.java:748)

...(此处省略若干行)


===================================================
"B":
        at com.yunyang.javacoder.sync.DeadLock.lambda$main$1(DeadLock.java:41)

        - waiting to lock <0x000000076baa67e0> (a java.lang.Object)
        - locked <0x000000076baa67f0> (a java.lang.Object)
        at com.yunyang.javacoder.sync.DeadLock$$Lambda$2/1419810764.run(Unknow
n Source)
        at java.lang.Thread.run(Thread.java:748)
"A":
        at com.yunyang.javacoder.sync.DeadLock.lambda$main$0(DeadLock.java:27)

        - waiting to lock <0x000000076baa67f0> (a java.lang.Object)
        - locked <0x000000076baa67e0> (a java.lang.Object)
        at com.yunyang.javacoder.sync.DeadLock$$Lambda$1/913190639.run(Unknown
 Source)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

4.3.3 分析线程快照

  • BLOCKED 状态:表示线程正在等待获取某个对象的锁。
  • waiting to lock:表示线程正在等待获取某个对象的锁。
  • locked:表示线程已经持有某个对象的锁。

如果发现两个或多个线程相互等待对方持有的锁,则可能发生了死锁。例如,在上面的输出示例中,A 等待 B 持有的锁,而 B 等待 A 持有的锁,这表明可能发生了死锁。

4.3.4 示例:死锁代码

以下是一个简单的死锁示例代码:

public class DeadLock {

    static Object a = new Object();
    static Object b = new Object();

    public static void main(String[] args) {
        new Thread(()->{
            synchronized (a) {
                System.out.println(Thread.currentThread().getName() + " 持有锁a,试图获取锁b");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (b){
                    System.out.println(Thread.currentThread().getName() + " 获取锁b");
                }
            }
        },"A").start();

        new Thread(()->{
            synchronized (b) {
                System.out.println(Thread.currentThread().getName() + " 持有锁b,试图获取锁a");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (a){
                    System.out.println(Thread.currentThread().getName() + " 获取锁a");
                }
            }
        },"B").start();
    }

}
  • 运行结果:
A 持有锁a,试图获取锁b
B 持有锁b,试图获取锁a

4.3.5 验证死锁

  1. 运行程序:运行上述死锁示例代码。
  2. 获取PID:使用 jps 命令获取Java进程的PID。
  3. 生成线程快照:使用 jstack <PID> 命令生成线程快照。
  4. 分析线程快照:检查线程快照中的 BLOCKED 状态和 waiting to lock 信息,确认是否存在死锁。

4.3.6 总结

通过使用 jpsjstack 工具,可以方便地验证Java程序中是否存在死锁。jps 用于列出Java进程,jstack 用于生成线程快照,通过分析线程快照中的 BLOCKED 状态和 waiting to lock 信息,可以判断是否发生了死锁。

5 附录 思维导图

在这里插入图片描述

6 参考链接

【【尚硅谷】大厂必备技术之JUC并发编程】

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AndroidStudio导入so文件

点击app 右键依次选择New-Floder-JNI Floder 创建jni目录 将需要的so文件拷贝到jni目录 在app目录下&#xff0c;build.gradle文件的android{}中添加&#xff1a; sourceSets {main{jniLibs.srcDirs [src/main/jni]}}点击一下Sync Project with Gradle Files 然后编译生成AP…

第7届医联赛圆满举行,鱼跃医疗连续6年保障赛事安全

9月22日-26日&#xff0c;第7届医联赛戈壁群英会在甘肃瓜州举行&#xff0c;来自全国各地的3000名医药行业企业家代表齐聚一堂&#xff0c;开启为期3天的身心极限挑战——徒步穿越81公里的戈壁滩。鱼跃医疗连续6年全程保障参赛者的生命安全&#xff0c;助力赛事圆满完成。 鱼跃…

大模型初学者指南:笔记本电脑上用 ollama 运行大模型!

之前国外有一初学者小哥在Google Colab&#xff08;A100 40G&#xff09;利用DPO微调Mistral 7B&#xff0c;微调后的模型在基准测试中取得不错的成绩&#xff0c;从那时起&#xff0c;我觉得在笔记本电脑上运行/微调大模型并非遥不可及的。 对于初学者而言&#xff0c;如果能…

[论文精读]Multi-Channel Graph Neural Network for Entity Alignment

论文网址&#xff1a;Multi-Channel Graph Neural Network for Entity Alignment (aclanthology.org) 论文代码&#xff1a;https:// github.com/thunlp/MuGNN 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&a…

插上网线无法连接网络,控制面板以太网消失 | 如何重装网络驱动

如果你确定你的网线没问题&#xff0c;网线插口没问题&#xff0c;那你大概率就是驱动问题&#xff0c;可以试一下本方法。 0 以太网消失 事情是这样的&#xff0c;我工作时候需要接内网&#xff0c;插网线&#xff0c;摸鱼时候连外网&#xff0c;我就把网线关了。 每次插网线…

飞致云开源社区月度动态报告(2024年9月)

自2023年6月起&#xff0c;中国领先的开源软件公司FIT2CLOUD飞致云以月度为单位发布《飞致云开源社区月度动态报告》&#xff0c;旨在向广大社区用户同步飞致云旗下系列开源软件的发展情况&#xff0c;以及当月主要的产品新版本发布、社区运营成果等相关信息。 飞致云开源运营…

STM32F1+HAL库+FreeTOTS学习13——二值信号量

STM32F1HAL库FreeTOTS学习13——二值信号量 1. 信号量2. 二值信号量3. 相关API函数3.1 创建二值信号量3.2 获取信号量3.3 释放信号量3.4 删除信号量 4. 二值信号量操作实验1. 实验内容2. 代码实现&#xff1a;3. 运行结果 上一期我们学习了FreeRTOS中的队列&#xff0c;这一期我…

【Linux探索学习】第二弹——Linux的基础指令(中)——夯实基础第二篇

Linux基础指令&#xff08;上&#xff09;&#xff1a;【Linux探索学习】第一弹——Linux的基本指令&#xff08;上&#xff09;——开启Linux学习第一篇-CSDN博客 前言&#xff1a; 在前面我们已经讲解了一些常用的Linux的基础指令&#xff0c;那些当然是远远不够的&#xff…

舵机在无人机中的应用

一、舵机工作原理 舵机是一种位置&#xff08;角度&#xff09;伺服的驱动器&#xff0c;由电子控制与机械控制部分组成。当控制信号输入时&#xff0c;电子控制部分会根据控制器的指令调节直流电机输出的旋转角度和速度&#xff0c;由机械部分转化为控制面的位移以及相应的角…

基于SpringBoot+Vue的个人健康管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

基于Python可视化的学习系统的设计与实现(源码+文档+调试+答疑)

文章目录 一、项目介绍二、视频展示三、开发环境四、系统展示五、代码展示六、项目文档展示七、项目总结 大家可以帮忙点赞、收藏、关注、评论啦 &#x1f447;&#x1f3fb; 一、项目介绍 随着计算机技术发展&#xff0c;计算机系统的应用已延伸到社会的各个领域&#xff0c…

小程序原生-利用setData()对不同类型的数据进行增删改

1. 声明和绑定数据 wxml文件 <view> {{school}} </view> <view>{{obj.name}}</view> <view id"{{id}}" > 绑定属性值 </view> <checkbox checked"{{isChecked}}"/> <!--算数运算--> <view>{{ id …

TS系列(7):知识点汇总

你好&#xff0c;我是沐爸&#xff0c;欢迎点赞、收藏、评论和关注。 一、TS是什么&#xff1f; TypeScript 由微软开发&#xff0c;是基于 JavaScript 的一个扩展语言。TypeScript 包含 JavaScript 的所有内容&#xff0c;是 JavaScript 的超集。TypeScript 增加了静态类型检…

基于SSM的宠物领养管理系统的设计与实现 (含源码+sql+视频导入教程+文档+PPT)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的宠物领养管理系统2拥有两种角色 管理员&#xff1a;宠物分类管理、领养管理、宠物商品管理、用户管理、寄存管理、丢失信息管理、订单管理等 用户&#xff1a;登录注册、收藏评…

算法:LCR 173. 点名 (原:剑指 offer:0~n-1 中缺失的数字 )

题目 链接&#xff1a;leetcode链接 思路分析&#xff08;一题多解&#xff09; 思路一&#xff1a;高斯求和公式 利用高斯求和公式求出0~n的和&#xff0c;然后减去nums数组中的每一个数&#xff0c;最后的结果就是缺失的数字 时间复杂度&#xff0c;O(N) 过于简单&#xff…

nginx:反向代理服务器——一个非常强大且灵活的工具

Nginx 是一个高性能的 HTTP 和反向代理服务器&#xff0c;常用于 Web 服务器、负载均衡器和反向代理。它以其高性能、稳定性、丰富的功能集、简单的配置文件和低资源消耗而著称。在部署 Web 应用程序时&#xff0c;Nginx 常被用来处理静态文件、反向代理动态内容、负载均衡等任…

AI大模型面试大纲

大纲 1. 介绍和背景 自我介绍&#xff08;5分钟&#xff09; 了解候选人的教育背景、工作经历和对大模型架构的兴趣。 2. 基础理论和概念&#xff08;30分钟&#xff09; 机器学习基础 解释基本概念&#xff0c;如监督学习、无监督学习和强化学习。 讨论不同的模型类型&#xf…

UG NX二次开发(C#)-建模-根据拉伸体获取草图对象

文章目录 1、前言2、在UG NX中创建基于草图的拉伸对象2.1 在建模模块中进入草图环境2.2 创建拉伸特征2.3 分析拉伸特征父项3 实现代码3.1 基于NXOpen实现3.2 UFun函数实现3.3 效果1、前言 今天在QQ群中,有个群友咨询了根据拉伸体获取草图对象,我今天难得清闲一次,就讲一下吧…

JQuery基本介绍和使用方法

JQuery基本介绍和使用方法 W3C 标准给我们提供了⼀系列的函数, 让我们可以操作: ⽹⻚内容⽹⻚结构⽹⻚样式 但是原⽣的JavaScript提供的API操作DOM元素时, 代码⽐较繁琐, 冗⻓. 我们可以使⽤JQuery来操作⻚⾯对象. jQuery是⼀个快速、简洁且功能丰富的JavaScript框架, 于20…

这几种文件找回方法你都知道吗?

一、基础方法 检查回收站 无论是Windows系统还是Mac系统&#xff0c;回收站&#xff08;或废纸篓&#xff09;都是文件被删除后的默认存放位置。可以打开回收站&#xff0c;查看是否有误删的文件&#xff0c;并右键单击选择“还原”来恢复。利用文件历史记录或备份 Windows系统…