Java中锁的全面详解(深刻理解各种锁)

news2024/11/14 20:24:15

一.Monitor

1. Java对象头

以32位虚拟机位例

对于普通对象,其对象头的存储结构为

总长为64位,也就是8个字节, 存在两个部分

  • Kclass Word: 其实也就是表示我们这个对象属于什么类型,也就是哪个类的对象.
  • 而对于Mark Word.查看一下它的结构存储

64位虚拟机中

而对于数组对象,我们将会多出32位来存储数组的长度.

2. Monitor原理

Monitor也就是管程,监视器,其实也就是我们熟知的锁.

每个Java对象都可以关联一个Monitor对象,如果使用synchronized给对象上锁(重量级)之后,该对象头的Mark Word中就被设置指向Monitor对象的指针.(只有重量级锁才会关联Monitor)

也就是如下图所示:

  • 每个对象都拥有一把锁,假设此时锁对象为Object.class对象.此时Thread-2拿到锁,会将自己的Mark Word指向对应的Monitor.也就是像这样(JDK6以前为重量级锁).此时会将自己原有的,例如之前的年龄age,hashcode等存储到Monitor中.

  • 其他线程来抢占锁,突然发现锁的Monitor中的Owner属性不为null.则进入阻塞状态.
  • 等Thread-2中代码执行完毕,将锁释放.Monitor将会唤醒所有阻塞的线程,并把之前存储的原有Object.class中对象头中的属性重新赋值给Object.class.此时其他线程就可以来抢占锁.(不公平的抢占)

注意:

  1. synchronized 必须是进入同一个对象的 monitor 才有上述的效果
  2. 不加 synchronized 的对象不会关联监视器,不遵从以上规则

字节码角度分析:

对于这样一段代码

我们查看对应的字节码指令,以来理解这个过程.

以上便是sybchronized过去的原理.

二.synchronized优化原理

而在从JDK6开始,HotSpot虚拟机开发团队对Java中的锁进行优化,

如增加了适应性自旋、锁消除、锁粗化、轻量级锁和偏向锁等优化策略,提升了 synchronized的性能.

1. 轻量级锁

轻量级锁的使用场景:如果一个对象虽然有多线程要加锁,但加锁的时间是错开的(也就是没有竞争),那么可以 使用轻量级锁来优化。

创建锁记录(Lock Record)对象,每个线程都的栈帧都会包含一个锁记录的结构,内部可以存储锁定对象的 Mark Word

让锁记录中 Object reference 指向锁对象,并尝试用 cas 替换 Object 的 Mark Word,将 Mark Word 的值存入锁记录

如果 cas 替换成功,对象头中存储了 锁记录地址和状态 00 ,表示由该线程给对象加锁,这时图示如下

如果 cas 失败,有两种情况

  1. 如果是其它线程已经持有了该 Object 的轻量级锁,这时表明有竞争,进入锁膨胀过程
  2. 如果是自己执行了 synchronized 锁重入,那么再添加一条 Lock Record 作为重入的计数

当退出 synchronized 代码块(解锁时)如果有取值为 null 的锁记录,表示有重入,这时重置锁记录,表示重入计数减一

当退出 synchronized 代码块(解锁时)锁记录的值不为 null,这时使用 cas 将 Mark Word 的值恢复给对象头

  • 成功,则解锁成功
  • 失败,说明轻量级锁进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程

总的来说就是:

1. 进行加锁操作时,jvm会判断是否已经是重量级锁,如果不是,则会在当前线程

栈帧中划出一块空间,作为该锁的锁记录,并且将锁对象MarkWord复制到该锁

记录中

2. 复制成功之后,jvm使用CAS操作将对象头MarkWord更新为指向锁记录的指针,

并将锁记录里的owner指针指向对象头的MarkWord。如果成功,则执行‘3’,否则

执行‘4’

3. 更新成功,则当前线程持有该对象锁,并且对象MarkWord锁标志设置为‘00’,

即表示此对象处于轻量级锁状态

4. 更新失败,jvm先检查对象MarkWord是否指向当前线程栈帧中的锁记录,如果是

则执行‘5’,否则执行‘6’

5. 表示锁重入;然后当前线程栈帧中增加一个锁记录第一部分(Displaced Mark

Word)为null,并指向Mark Word的锁对象,起到一个重入计数器的作用。

6. 表示该锁对象已经被其他线程抢占,则进行自旋等待(默认10次),等待次数

达到阈值仍未获取到锁,则升级为重量级锁(会由另一个线程为锁对象申请Monitor,也就是接下来的锁膨胀)

2.可重入锁

synchronized可重入的实现原理(优化过后)

  1. 锁对象与计数器
    • 每个Java对象都有一个与之关联的监视器(Monitor)。这个监视器内部维护了一个计数器(通常称为recursions变量),用于记录当前线程获取该对象锁的次数。
    • 当线程首次进入synchronized修饰的代码块或方法时,会尝试获取对象的锁。如果锁未被占用(即计数器为0),则线程获取锁并将计数器设置为1。(与之前再创造一个Lock Record不同)
  1. 重入机制
    • 如果当前线程已经持有了该对象的锁(即该线程已经执行了synchronized修饰的代码块或方法),当再次尝试进入另一个由相同对象锁保护的synchronized代码块或方法时,JVM会检查持有锁的线程是否为当前线程。
    • 如果是,则JVM允许该线程再次获取锁,并将计数器的值加1,而不是让线程等待锁被释放。这个过程就是synchronized的可重入性。
  1. 锁的释放
    • 当线程退出synchronized修饰的代码块或方法时,JVM会自动将对象锁的计数器减1
    • 如果计数器减至0,则表示当前线程已经释放了该对象的锁,其他等待获取该锁的线程将有机会获取锁并进入相应的synchronized代码块或方法。

2. 锁膨胀

如果在尝试加轻量级锁的过程中,CAS 操作无法成功,这时一种情况就是有其它线程为此对象加上了轻量级锁(有竞争),这时需要进行锁膨胀,将轻量级锁变为重量级锁.

static Object obj = new Object();
public static void method1() {
    synchronized( obj ) {
        // 同步块
    }
}
  • 当 Thread-1 进行轻量级加锁时,Thread-0 已经对该对象加了轻量级锁

这时 Thread-1 加轻量级锁失败,进入锁膨胀流程

  • 即为 Object 对象申请 Monitor 锁,让 Object 指向重量级锁地址
  • 然后自己进入 Monitor 的 EntryList BLOCKED

  • 当 Thread-0 退出同步块解锁时,使用 cas 将 Mark Word 的值恢复给对象头,失败。这时会进入重量级解锁流程,即按照 Monitor 地址找到 Monitor 对象,设置 Owner 为 null,唤醒 EntryList 中 BLOCKED 线程

3. 自旋锁

重量级锁竞争的时候,还可以使用自旋来进行优化,如果当前线程自旋成功(即这时候持锁线程已经退出了同步块,释放了锁),这时当前线程就可以避免阻塞。

自旋重试成功的情况

自旋重试失败的情况

注意:

  1. 自旋会占用 CPU 时间,单核 CPU 自旋就是浪费,多核 CPU 自旋才能发挥优势。
  2. 在 Java 6 之后自旋锁是自适应的,比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会高,就多自旋几次;反之,就少自旋甚至不自旋,总之,比较智能。
  3. Java 7 之后不能控制是否开启自旋功能

4. 偏向锁

轻量级锁在没有竞争时(就自己这个线程),每次重入仍然需要执行 CAS 操作。

Java 6 中引入了偏向锁来做进一步优化:只有第一次使用 CAS 将线程 ID 设置到对象的 Mark Word 头,之后发现 这个线程 ID 是自己的就表示没有竞争,不用重新 CAS。以后只要不发生竞争,这个对象就归该线程所有.可以理解为,偏向锁就是JVM认为这把锁就是属于一个线程

static final Object obj = new Object();
public static void m1() {
 synchronized( obj ) {
 // 同步块 A
 m2();
 }
}
public static void m2() {
 synchronized( obj ) {
 // 同步块 B
 m3();
 }
}
public static void m3() {
 synchronized( obj ) {
 }
}

4.1 偏向状态

一个对象创建时:

  • 如果开启了偏向锁(默认开启),那么对象创建后,markword 值为 0x05 即最后3位,为101这时它的thread、epoch、age 都为 0
  • 偏向锁是默认是延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加 VM 参数

-XX:BiasedLockingStartupDelay=0 来禁用延迟

  • 如果没有开启偏向锁,那么对象创建后,markword 值为 0x01 即最后 3 位为 001,这时它的 hashcode、 age 都为 0,第一次用到 hashcode 时才会赋值

 

// 添加虚拟机参数 -XX:BiasedLockingStartupDelay=0 
public static void main(String[] args) throws IOException {
    Dog d = new Dog();
    ClassLayout classLayout = ClassLayout.parseInstance(d);
    new Thread(() -> {
        log.debug("synchronized 前");
        System.out.println(classLayout.toPrintableSimple(true));
        synchronized (d) {
            log.debug("synchronized 中");
            System.out.println(classLayout.toPrintableSimple(true));
        }
        log.debug("synchronized 后");
        System.out.println(classLayout.toPrintableSimple(true));
    }, "t1").start();
}

输出

11:08:58.117 c.TestBiased [t1] - synchronized 前
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000101 
11:08:58.121 c.TestBiased [t1] - synchronized 中
00000000 00000000 00000000 00000000 00011111 11101011 11010000 00000101 
11:08:58.121 c.TestBiased [t1] - synchronized 后
00000000 00000000 00000000 00000000 00011111 11101011 11010000 00000101

注意:

处于偏向锁的对象解锁后,线程 id 仍存储于对象头中

测试禁用

在上面测试代码运行时在添加 VM 参数 -XX:-UseBiasedLocking 禁用偏向锁

输出

11:13:10.018 c.TestBiased [t1] - synchronized 前 
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
11:13:10.021 c.TestBiased [t1] - synchronized 中 
00000000 00000000 00000000 00000000 00100000 00010100 11110011 10001000 
11:13:10.021 c.TestBiased [t1] - synchronized 后 
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
4.2 撤销 - 调用对象 hashCode

调用了对象的 hashCode, 但偏向锁的对象 MarkWord 中存储的是线程 id,如果调用 hashCode 会导致偏向锁被撤销

  • 轻量级锁会在锁记录中记录 hashCode
  • 重量级锁会在 Monitor 中记录 hashCode

所以由于偏向锁没有存储空间来存储hashCode的值,只能撤销.(仔细查看偏量锁的对象头结构)

  • 在调用 hashCode 后使用偏向锁,记得去掉 -XX:-UseBiasedLocking(禁用默认偏向锁.)

输出

11:22:10.386 c.TestBiased [main] - 调用 hashCode:1778535015 
11:22:10.391 c.TestBiased [t1] - synchronized 前
00000000 00000000 00000000 01101010 00000010 01001010 01100111 00000001 
11:22:10.393 c.TestBiased [t1] - synchronized 中
00000000 00000000 00000000 00000000 00100000 11000011 11110011 01101000 
11:22:10.393 c.TestBiased [t1] - synchronized 后
00000000 00000000 00000000 01101010 00000010 01001010 01100111 00000001
4.3 撤销 - 其它线程使用对象

当有其它线程使用偏向锁对象时,会将偏向锁升级为轻量级锁

private static void test2() throws InterruptedException {
    Dog d = new Dog();
    Thread t1 = new Thread(() -> {
    synchronized (d) {
        log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
    }
    synchronized (TestBiased.class) {
        TestBiased.class.notify();
    }
    // 如果不用 wait/notify 使用 join 必须打开下面的注释
    // 因为:t1 线程不能结束,否则底层线程可能被 jvm 重用作为 t2 线程,底层线程 id 是一样的
    /*try {
     System.in.read();
     } catch (IOException e) {
     e.printStackTrace();
     }*/
    }, "t1");
    t1.start();
    Thread t2 = new Thread(() -> {
    synchronized (TestBiased.class) {
        try {
            TestBiased.class.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
    synchronized (d) {
        log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
    }
    log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
    }, "t2");
    t2.start();
}

输出

[t1] - 00000000 00000000 00000000 00000000 00011111 01000001 00010000 00000101 
[t2] - 00000000 00000000 00000000 00000000 00011111 01000001 00010000 00000101 
[t2] - 00000000 00000000 00000000 00000000 00011111 10110101 11110000 01000000 
[t2] - 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
4.4 撤销 - 调用 wait/notify
public static void main(String[] args) throws InterruptedException {
    Dog d = new Dog();
    Thread t1 = new Thread(() -> {
        log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
        synchronized (d) {
            log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
            try {
                d.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
        }
    }, "t1");
    t1.start();
    new Thread(() -> {
        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (d) {
            log.debug("notify");
            d.notify();
        }
    }, "t2").start();
}

输出

[t1] - 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000101 
[t1] - 00000000 00000000 00000000 00000000 00011111 10110011 11111000 00000101 
[t2] - notify 
[t1] - 00000000 00000000 00000000 00000000 00011100 11010100 00001101 11001010

4.5 批量重偏向

如果对象虽然被多个线程访问,但没有竞争,这时偏向了线程 T1 的对象仍有机会重新偏向 T2,重偏向会重置对象的Thread ID .

当撤销偏向锁阈值超过 20 次后,jvm 会这样觉得有可能偏向错了,于是会在给这些对象加锁时重新偏向至加锁线程.

private static void test3() throws InterruptedException {
    Vector<Dog> list = new Vector<>();
    Thread t1 = new Thread(() -> {
    for (int i = 0; i < 30; i++) {
        Dog d = new Dog();
        list.add(d);
        synchronized (d) {
            log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
        }
    }
    synchronized (list) {
        list.notify();
    } 
    }, "t1");
    t1.start();

    Thread t2 = new Thread(() -> {
    synchronized (list) {
        try {
            list.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    log.debug("===============> ");
    for (int i = 0; i < 30; i++) {
        Dog d = list.get(i);
        log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
        synchronized (d) {
            log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
        }
        log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
    }
    }, "t2");
    t2.start();
}

输出(只取对应了的部分)

[t1] - 19 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101


[t2] - ===============> (注意到19次的变化,没有撤销)
[t2] - 17 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101 
[t2] - 17 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000 
[t2] - 17 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
[t2] - 18 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101 
[t2] - 18 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000 
[t2] - 18 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
[t2] - 19 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101 
[t2] - 19 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101 
[t2] - 19 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101 
[t2] - 20 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101 
[t2] - 20 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101 
[t2] - 20 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101 
[t2] - 21 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101 
[t2] - 21 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101 
[t2] - 21 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101 
[t2] - 22 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101 
[t2] - 22 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101 
[t2] - 22 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
4.6 批量撤销

当撤销偏向锁阈值超过 40 次后,jvm 会这样觉得,自己确实偏向错了,根本就不该偏向。于是整个类的所有对象都会变为不可偏向的,新建的对象也是不可偏向的.(第二个循环擦除了20次,第三个循环擦除了20次.这两个循环前20次都是擦除,然后锁重偏向,后19次都在锁偏向,即不会擦除)

private static void test4() throws InterruptedException {
    Vector<Dog> list = new Vector<>();
    int loopNumber = 39;
    t1 = new Thread(() -> {
    for (int i = 0; i < loopNumber; i++) {
        Dog d = new Dog();
        list.add(d);
        synchronized (d) {
            log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
        }
    }
    LockSupport.unpark(t2);
    }, "t1");
    t1.start();
    t2 = new Thread(() -> {
    LockSupport.park();
    log.debug("===============> ");
    for (int i = 0; i < loopNumber; i++) {
        Dog d = list.get(i);
        log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
        synchronized (d) {
            log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
        }
        log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
    }
    LockSupport.unpark(t3);
    }, "t2");
    t2.start();
    t3 = new Thread(() -> {
    LockSupport.park();
    log.debug("===============> ");
    for (int i = 0; i < loopNumber; i++) {
        Dog d = list.get(i);
        log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
        synchronized (d) {
            log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
        }
        log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
    }
    }, "t3");
    t3.start();
    t3.join();
    log.debug(ClassLayout.parseInstance(new Dog()).toPrintableSimple(true));
}

5. 锁消除

锁消除,Java虚拟机中的即时编译器会自动帮我们优化,不存在锁共享的情况会直接将锁擦除.

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

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

相关文章

阿里云开源 Qwen2-Audio 音频聊天和预训练大型音频语言模型

Qwen2-Audio由阿里巴巴集团Qwen团队开发&#xff0c;它能够接受各种音频信号输入&#xff0c;对语音指令进行音频分析或直接文本回复。与以往复杂的层次标签不同&#xff0c;Qwen2-Audio通过使用自然语言提示简化了预训练过程&#xff0c;并扩大了数据量。 喜好儿网 Qwen2-Au…

六边形动态特效404单页HTML源码

源码介绍 动态悬浮的六边形,旁边404文字以及跳转按钮,整体看着像科技二次元画风,页面简约美观,可以做网站错误页或者丢失页面,将下面的代码放到空白的HTML里面,然后上传到服务器里面,设置好重定向即可 效果预览 完整源码 <!DOCTYPE html> <html><head…

【VScode】安装【ESP-IDF】插件及相关工具链

一、ESP-IDF简介 二、VScode安装ESP-IDF插件 三、安装ESP-IDF、ESP-IDF-Tools以及相关工具链 四、测试例程&编译烧录 一、ESP-IDF简介 二、VScode安装ESP-IDF插件 【VScode】安装配置、插件及远程SSH连接 【VSCode】自定义配置 打开VScode&#xff0c;在插件管理搜索esp…

关于Linux的面试题(实时更新中~)

一、软连接和硬连接的区别&#xff1a; 软连接创建方式 ln -s 被链接文件 链接文件 &#xff08;1&#xff09;软链接是一个链接文件&#xff1b; &#xff08;2&#xff09;软链接有着自己的 inode 号&#xff08;文件编号&#xff09;&#xff1b; &#xff08;3&#…

怎么压缩pdf文件大小?分享8款便捷的PDF压缩工具分享

当用户上传PDF文件到网站时&#xff0c;常常会遇到文件大小的限制问题。尤其是当PDF文件包含大量图片、图形和丰富内容时&#xff0c;文件体积会变得很大。这时&#xff0c;为了符合网站的大小要求并成功上传&#xff0c;我们需要对PDF文件进行压缩。那么&#xff0c;如何将PDF…

NXP i.MX8系列平台开发讲解 - 3.19 Linux TTY子系统(二)

专栏文章目录传送门&#xff1a;返回专栏目录 Hi, 我是你们的老朋友&#xff0c;主要专注于嵌入式软件开发&#xff0c;有兴趣不要忘记点击关注【码思途远】 目录 1. Linux 串口驱动 1.1 Uart 驱动注册流程 1.2 uart 操作函数 1.3 line discipline 2. Linux tty应用层使用…

持续集成07--Jenkins配置Allure测试报告

前言 在持续集成&#xff08;CI&#xff09;流程中&#xff0c;自动化测试报告是评估软件质量和追踪问题的重要工具。Allure Framework作为一个轻量级且功能丰富的测试报告工具&#xff0c;能够生成详细的测试报告&#xff0c;帮助团队更好地理解测试结果。本章节“持续集成07-…

Spring Boot集成kudu快速入门Demo

1.什么是kudu 在Kudu出现前&#xff0c;由于传统存储系统的局限性&#xff0c;对于数据的快速输入和分析还没有一个完美的解决方案&#xff0c;要么以缓慢的数据输入为代价实现快速分析&#xff0c;要么以缓慢的分析为代价实现数据快速输入。随着快速输入和分析场景越来越多&a…

06MFC之对话框--重绘元文件

文章目录 实现示例展示需要绘制的窗口/位置控件位置更新下一次示例粗细滑动部分更新重绘元文件(窗口变化内容消失)方法一:使用元文件方法二:兼容设备方法三:使用自定义类存储绘图数据除画笔外功能处理画笔功能处理保存前面画的线及色彩实现示例展示 需要绘制的窗口/位置 …

Kafka Producer发送消息流程之消息异步发送和同步发送

文章目录 1. 异步发送2. 同步发送 1. 异步发送 Kafka默认就是异步发送&#xff0c;在Main线程中的多条消息&#xff0c;没有严格的先后顺序&#xff0c;Sender发送后就继续下一条&#xff0c;异步接受结果。 public class KafkaProducerCallbackTest {public static void mai…

k8s集群 安装配置 Prometheus+grafana+alertmanager

k8s集群 安装配置 Prometheusgrafanaalertmanager k8s环境如下&#xff1a;机器规划&#xff1a; node-exporter组件安装和配置安装node-exporter通过node-exporter采集数据显示192.168.40.180主机cpu的使用情况显示192.168.40.180主机负载使用情况 Prometheus server安装和配置…

如何用AI交互数字人一体机,打造政务服务新名片?

如今&#xff0c;将“高效办成一件事”作为优化政务服务、提升行政效能的重要抓手&#xff0c;各地方为了促进政务服务由传统模式向数字化、智能化方向转变&#xff0c;纷纷在政务服务场景融合了AI交互数字人&#xff0c;实现“无人化、智慧化”导办、帮办、代办等模式&#xf…

深度学习程序环境配置

深度学习环境配置 因为之前轻薄本没有显卡跑不起来&#xff0c;所以换了台电脑重新跑程序&#xff0c;故记录一下配置环境的步骤及常见错误 本人数学系&#xff0c;计算机部分知识比较匮乏&#xff0c;计算机专业同学可以略过部分内容 深度学习环境配置 深度学习环境配置 CUD…

Vue脚手架安装(保姆级)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…

WEB前端05-JavaScrip基本对象

JavaScript对象 1.Function对象 函数的创建 //方法一&#xff1a;自定义函数 function 函数名([参数]) {函数体[return 表达式] }//方法二&#xff1a;匿名函数 (function([参数]) {函数体[return 表达式] }); **使用场景一&#xff1a;定义后直接调用使用(只使用一次) (fun…

【Arduino IDE】安装及开发环境、ESP32库

一、Arduino IDE下载 二、Arduino IDE安装 三、ESP32库 四、Arduino-ESP32库配置 五、新建ESP32-S3N15R8工程文件 乐鑫官网 Arduino官方下载地址 Arduino官方社区 Arduino中文社区 一、Arduino IDE下载 ESP-IDF、MicroPython和Arduino是三种不同的开发框架&#xff0c;各自适…

如何防范场外个股期权的交易风险?

场外个股期权交易&#xff0c;作为金融衍生品市场的重要组成部分&#xff0c;为投资者提供了更为灵活和多样化的投资策略。然而&#xff0c;其高杠杆、高风险特性也使得投资者在追求高收益的同时&#xff0c;面临着较大的交易风险。为了有效防范这些风险&#xff0c;投资者需要…

达梦 ./disql SYSDBA/SYSDBA报错[-70028]:创建SOCKET连接失败. 解决方法

原因 达梦命令./disql SYSDBA/SYSDBA默认访问端口5236&#xff0c;如果初始化实例的时候修改了端口&#xff0c;需要指定端口访问 解决 ./disql SYSDBA/SYSDBA192.168.10.123:5237

手机如何伪装ip网络地址

伪装IP地址是指通过技术手段修改网络设备的IP地址&#xff0c;使其看起来像是来自另一个网络位置。这种技术通常用于隐藏真实的网络活动&#xff0c;以保护隐私。那么&#xff0c;手机如何伪装IP网络地址&#xff1f; 要在手机上伪装IP地址&#xff0c;‌可以通过下载和安装手机…

阿里云国际站:海外视频安全的DRM加密

随着科技的进步&#xff0c;视频以直播或录播的形式陆续开展海外市场&#xff0c;从而也衍生出内容安全的问题&#xff0c;阿里云在这方面提供了完善的内容安全保护机制&#xff0c;适用于不同的场景&#xff0c;如在视频安全提供DRM加密。 由图可以了解到阿里云保护直播安全的…