多线程学习篇四:synchronized

news2024/11/24 23:07:18

1. synchronized 的使用

1.1 作用于实例方法

@Slf4j(topic = "c.Test01")
public class Test01 {
    public synchronized void method1() {
        // 代码逻辑
    }
}

等价于下列写法: 

@Slf4j(topic = "c.Test01")
public class Test01 {
    public void method1() {
        synchronized (this) {
            // 代码逻辑
        }
    }
}

1.2 作用于静态方法

@Slf4j(topic = "c.Test02")
public class Test02 {
    public synchronized static void method1() {
       // 代码逻辑
    }
}

等价于下列写法: 

@Slf4j(topic = "c.Test02")
public class Test02 {
    public static void method1() {
        synchronized (Test02.class) {
            // 代码逻辑
        }
    }
}

2. “线程八锁” 

2.1 案例1

@Slf4j(topic = "c.Lock01")
public class Lock01 {
    public synchronized void method1() {
        log.info("m1");
    }

    public synchronized void method2() {
        log.info("m2");
    }

    public static void main(String[] args) {
        Lock01 lock01 = new Lock01();
        new Thread(lock01::method1).start();
        new Thread(lock01::method2).start();
    }
}

输出结果:

  • case1:m1 → m2
  • case2:m2 → m1

2.2 案例2

@Slf4j(topic = "c.Lock02")
public class Lock02 {
    public synchronized void method1() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("m1");
    }

    public synchronized void method2() {
        log.info("m2");
    }

    public static void main(String[] args) {
        Lock02 lock02 = new Lock02();
        new Thread(lock02::method1).start();
        new Thread(lock02::method2).start();
    }
}

输出结果:

  • case1:1s后 → m1 → m2
  • case2:m2 → 1s后 → m1

2.3 案例3

@Slf4j(topic = "c.Lock03")
public class Lock03 {
    public synchronized void method1() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("m1");
    }

    public synchronized void method2() {
        log.info("m2");
    }

    public void method3() {
        log.info("m3");
    }

    public static void main(String[] args) {
        Lock03 lock03 = new Lock03();
        new Thread(lock03::method1).start();
        new Thread(lock03::method2).start();
        new Thread(lock03::method3).start();
    }
}

可能调用顺序:

  • method1 → method2 → method3:m3→ 1s后 → m1 → m2
  • method1 → method3 → method2:m3→ 1s后 → m1 → m2
  • method2 → method1 → method3:m2 → m3→ 1s后 → m1
  • method2 → method3 → method1:m2 → m3→ 1s后 → m1
  • method3 → method1 → method2:m3→ 1s后 → m1 → m2
  • method3 → method2 → method1:m3 → m2→ 1s后 → m1

输出结果(去重):

  • case1:m3→ 1s后 → m1 → m2
  • case2:m2 → m3→ 1s后 → m1
  • case2:m3 → m2→ 1s后 → m1

2.4 案例4

@Slf4j(topic = "c.Lock04")
public class Lock04 {
    public synchronized void method1() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("m1");
    }

    public synchronized void method2() {
        log.info("m2");
    }

    public static void main(String[] args) {
        Lock04 lock041 = new Lock04();
        Lock04 lock042 = new Lock04();

        new Thread(lock041::method1).start();
        new Thread(lock042::method2).start();
    }
}

输出结果:

  • case1:m2 → 1s后 → m1

2.5 案例5

@Slf4j(topic = "c.Lock05")
public class Lock05 {
    public synchronized void method1() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("m1");
    }

    public static synchronized void method2() {
        log.info("m2");
    }

    public static void main(String[] args) {
        Lock05 lk = new Lock05();

        new Thread(() -> lk.method1()).start();
        new Thread(() -> lk.method2()).start();
    }
}

输出结果:

  • case1:m2 → 1s后 → m1

2.6 案例6

@Slf4j(topic = "c.Lock06")
public class Lock06 {
    public static synchronized void method1() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("m1");
    }

    public static synchronized void method2() {
        log.info("m2");
    }

    public static void main(String[] args) {
        new Thread(Lock06::method1).start();
        new Thread(Lock06::method2).start();
    }
}

输出结果:

  • case1:1s后 → m1 → m2
  • case2:m2 → 1s后 → m1

2.7 案例7

@Slf4j(topic = "c.Lock07")
public class Lock07 {
    public synchronized void method1() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("m1");
    }

    public static synchronized void method2() {
        log.info("m2");
    }

    public static void main(String[] args) {
        Lock07 lk1 = new Lock07();
        Lock07 lk2 = new Lock07();

        new Thread(() -> lk1.method1()).start();
        new Thread(() -> lk2.method2()).start();
    }
}

输出结果:

  • case1:m2 → 1s后 → m1

2.8 案例8

@Slf4j(topic = "c.Lock08")
public class Lock08 {
    public static synchronized void method1() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("m1");
    }

    public static synchronized void method2() {
        log.info("m2");
    }

    public static void main(String[] args) {
        Lock08 lk1 = new Lock08();
        Lock08 lk2 = new Lock08();

        new Thread(() -> lk1.method1()).start();
        new Thread(() -> lk2.method2()).start();
    }
}

输出结果:

  • case1:1s后 → m1 → m2
  • case2:m2 → 1s后 → m1

3. Synchronized 进阶

3.1 对象头

3.1.1 32 位虚拟机
3.1.1.1 普通对象

3.1.1.2 数组对象

3.1.1.3 Mark Word

3.1.2 64 位虚拟机
3.1.2.1 普通对象

3.1.2.2 数组对象

3.1.2.3 Mark Word

3.2  Monitor原理

Monitor 被翻译为监视器管程,每个 Java 对象都可以关联一个 Monitor 对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的 Mark Word 中就被设置指向 Monitor 对象的指针

3.2.1 Monitor 结构

  • 刚开始 Monitor 中 Owner 为 null
  • 当 Thread-2 执行 synchronized(obj)就会将 Monitor 的所有者 Owner 置为 Thread-2,Monitor中只能有一个 Owner
  • 在 Thread-2 上锁的过程中,如果 Thread-3,Thread-4,Thread-5 也来执行synchronized(obj),就会进入EntryList(线程进入 BLOCKED 状态)
  • Thread-2 执行完同步代码块的内容,然后唤醒 EntryList 中等待的线程来竞争锁,竞争的时是非公平的
  • 图中 WaitSet 中的 Thread-0,Thread-1 是之前获得过锁,但条件不满足进入 WAITING 状态的线程,可以通过 notify / notifyAll 方法将线程从 WaitSet 转移到 EntryList 竞争锁

3.3 锁升级

3.3.1 轻量级锁(不涉及 Monitor )

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

public class Lock01 {

    private static final Object LOCK = new Object();

    public static void method1() {
        synchronized (LOCK) {
            // 同步块 A
            method2();
        }
    }

    public static void method2() {
        synchronized (LOCK) {
            // 同步块 B
        }
    }
}
3.3.1.1 创建锁记录(Lock Record)对象,每个线程的栈帧都会包含一个锁记录的结构,内部可以存储锁定对象的 Mark Word

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

3.3.1.3 CAS 分成两种情况
3.3.1.3.1 CAS 替换成功,对象头中存储了 锁记录地址和状态 00 ,表示由该线程给对象加锁,这时图示如下

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

3.3.1.4 当退出 synchronized 代码块(解锁时)如果有取值为 null 的锁记录,表示有重入,这时重置锁记录,表示重入计数减一
3.3.1.5 当退出 synchronized 代码块(解锁时)锁记录的值不为 null,这时使用 cas 将 Mark Word 的值恢复给对象头
  • 成功,则解锁成功
  • 失败,说明轻量级锁进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程
3.3.2 锁膨胀(轻量级锁膨胀为重量级锁)

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

3.3.2.1 当 Thread-1 进行轻量级加锁时,Thread-0 已经对该对象加了轻量级锁

3.3.2.2 这时 Thread-1 加轻量级锁失败,进入锁膨胀流程
  • 即为 Object 对象申请 Monitor 锁,让 Object 指向重量级锁地址
  • 然后自己进入 Monitor 的 EntryList(线程进入 BLOCKED 状态)

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

重量级锁竞争的时候,还可以使用自旋(循环尝试获取重量级锁)来进行优化,如果当前线程自旋成功(即这时候持锁线程已经退出了同步块,释放了锁),这时当前线程就可以避免阻塞。 (进入阻塞再恢复,会发生上下文切换,比较耗费性能)

3.3.3.1 自选成功情况
线程1(core1上)对象 Mark线程1(core1上)
-10 (重量锁)-
访问同步块,获取 monitor10 (重量锁)重量锁指针-
成功(加锁)10 (重量锁)重量锁指针-
执行同步块10 (重量锁)重量锁指针-
执行同步块10 (重量锁)重量锁指针访问同步块,获取 monitor
执行同步块10 (重量锁)重量锁指针自选重试
执行完毕10 (重量锁)重量锁指针自选重试
成功(解锁)00(无锁)自选重试
-10 (重量锁)重量锁指针访问同步块,获取 monitor
-10 (重量锁)重量锁指针成功(加锁)
-。。。。。。
3.3.3.2 自选失败情况
线程1(core1上)对象 Mark线程1(core1上)
-10 (重量锁)-
访问同步块,获取 monitor10 (重量锁)重量锁指针-
成功(加锁)10 (重量锁)重量锁指针-
执行同步块10 (重量锁)重量锁指针-
执行同步块10 (重量锁)重量锁指针访问同步块,获取 monitor
执行同步块10 (重量锁)重量锁指针自选重试
执行同步块10 (重量锁)重量锁指针自选重试
执行同步块10 (重量锁)重量锁指针自选重试
执行同步块10 (重量锁)重量锁指针阻塞
3.3.3.3 小结 
  • 自旋会占用 CPU 时间,单核 CPU 自旋就是浪费,多核 CPU 自旋才能发挥优势。
  • 在 Java 6 之后自旋锁是自适应的,比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会高,就多自旋几次;反之,就少自旋甚至不自旋,总之,比较智能。
  • Java 7 之后不能控制是否开启自旋功能
3.3.4 偏向锁(比轻量锁更轻的锁)

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

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

PS:这里的线程 id 是操作系统赋予的 id 和 Thread 的id是不同的

3.3.4.1 偏向状态

我们再回忆一下上文介绍的,64 位操作系统的 Mark 头

  • 如果开启了偏向锁(默认开启),那么对象创建后,markword 值为 0x05 即最后 3 位为 101,这时它的 thread、epoch、age 都为 0
  • 偏向锁是默认是延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加 VM 参数 -XX:BiasedLockingStartupDelay=0 来禁用延迟
  • 如果没有开启偏向锁,那么对象创建后,markword 值为 0x01 即最后 3 位为 001,这时它的 hashcode、age 都为 0,第一次用到 hashcode 时才会赋值

PS:VM 参数 -XX:-UseBiasedLocking 禁用偏向锁

3.3.4.2 测试偏向锁
3.3.4.2.1 添加依赖
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.17</version>
</dependency>
3.3.4.2.2 测试代码
@Slf4j(topic = "c.Lock02")
public class Lock02 {

    private static final Object OBJECT = new Object();

    public static void main(String[] args) throws Exception {
        log.info(ClassLayout.parseInstance(OBJECT).toPrintable());
        synchronized (OBJECT) {
            log.info(ClassLayout.parseInstance(OBJECT).toPrintable());
        }
        log.info(ClassLayout.parseInstance(OBJECT).toPrintable());
    }
}
3.3.4.2.3 测试结果

通过结果得出以下结论:

  • Mark Word:8 * 8 = 64 bits
  • Klass Word:4 * 8 = 32 bits
  • 对其填充:4 * 8 = 32 bits
  • Object Head : Mark Word + Klass Word = 96 bits
  • 其中 Mark Word 的最后三位是 101(5),即偏向状态。处于偏向锁的对象解锁后,线程 id 仍存储于对象头中,也就是偏向某个线程了
3.3.4.3 撤销偏向 -- 调用 hashcode 方法

正常状态对象一开始是没有 hashCode 的,第一次调用才生成,调用了 hashCode() 后会撤销该对象的偏向锁,效果演示如下:

@Slf4j(topic = "c.Lock02")
public class Lock02 {

    private static final Object OBJECT = new Object();

    public static void main(String[] args) throws Exception {

        OBJECT.hashCode();
        
        log.info(ClassLayout.parseInstance(OBJECT).toPrintable());
        synchronized (OBJECT) {
            log.info(ClassLayout.parseInstance(OBJECT).toPrintable());
        }
        log.info(ClassLayout.parseInstance(OBJECT).toPrintable());
    }
}

未加锁时 Mark Word 的最后三位是 001(1),即无锁状态。 加锁时 Mark Word 的最后三位是 000(0),即轻量锁。

3.3.4.4 撤销偏向 -- 两个线程错开时间加锁
@Slf4j(topic = "c.Lock03")
public class Lock03 {

    private static final Object OBJECT = new Object();

    public static void method() {
        log.info(ClassLayout.parseInstance(OBJECT).toPrintable());
        synchronized (OBJECT) {
            log.info(ClassLayout.parseInstance(OBJECT).toPrintable());
        }
        log.info(ClassLayout.parseInstance(OBJECT).toPrintable());
    }

    public static void main(String[] args) throws Exception {

        Thread t1 = new Thread(Lock03::method, "t1");
        Thread t2 = new Thread(Lock03::method, "t2");

        t1.start();
        TimeUnit.MILLISECONDS.sleep(500);
        t2.start();

        t1.join();
        t2.join();
    }
}

通过结果得出结论:

  • 101:偏向状态
  • 101:偏向状态,偏向线程1
  • 101:偏向状态,偏向线程1
  • 101:偏向状态,偏向线程2
  • 000:无偏向,线程2拥有轻量级锁
  • 001:无锁,撤销偏向
3.3.4.5 撤销偏向 -- 调用 wait/notify
@Slf4j(topic = "c.Lock04")
public class Lock04 {
    private static final Object OBJECT = new Object();

    public static void main(String[] args) throws Exception {

        Thread t1 = new Thread(() -> {
            log.info(ClassLayout.parseInstance(OBJECT).toPrintable());
            synchronized (OBJECT) {
                log.info(ClassLayout.parseInstance(OBJECT).toPrintable());
                try {
                    OBJECT.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.info(ClassLayout.parseInstance(OBJECT).toPrintable());
        }, "t1");

        Thread t2 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (OBJECT) {
                OBJECT.notify();
            }
        }, "t2");

        t1.start();
        TimeUnit.MILLISECONDS.sleep(500);
        t2.start();

        t1.join();
        t2.join();
    }
}

通过结果得出结论:

  • 101:偏向状态
  • 101:偏向状态,偏向线程1
  • 001:无锁,撤销偏向

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

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

相关文章

基于机器学习的虚假新闻智能检测系统

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长 QQ 名片 :) 1. 项目简介 随着互联网的普及和社交媒体的发展&#xff0c;虚假新闻&#xff08;fake news&#xff09;问题日益严重&#xff0c;对社会和个人产生了诸多负面影响。传统的新闻审核方法通常依赖于人工审核&…

基于gewechat制作第一个微信聊天机器人

Gewe 个微框架 GeWe&#xff08;个微框架&#xff09;是一个创新性的软件开发框架&#xff0c;为个人微信号以及企业信息安全提供了强大的功能和保障。GeWe的设计旨在简化开发过程&#xff0c;使开发者能够高效、灵活地构建和定制通信协议&#xff0c;以满足不同应用场景的需求…

SSL---SSL certificate problem

0 Preface/Foreword 0.1 SSL certificate problem 开发过程中&#xff0c;gitlab-runner连接gitlab时候出现SSL 证书问题。 场景&#xff1a;公司的gitlab runner服务器引入了SSL证书&#xff0c;每年都会主动更新一次。当前的gitlab-runner运行在PC机器上&#xff0c;但是g…

ZYNQ使用XGPIO驱动外设模块(前半部分)

目录 目录 一、新建BD文档&#xff0c;添加ZYNQ处理器 1.BD文档: 2.在Vivado中&#xff0c;BD文件的生成过程通常包括以下步骤&#xff1a; 1)什么是Tcl Console: 3.PL部分是FPGA可编程逻辑部分&#xff0c;它提供了丰富的IO资源&#xff0c;可以用于实现各种硬件接口和功…

刘文超数量关系笔记

第一章解题技巧 第一节代入排除法 代入排除是数量关系第一大法。 代入排除顾名思义是将答案选项代入原题目&#xff0c;与题意不符的选项即可排除&#xff0c; 最终得出正确答案。 优先使用代入排除的题型&#xff1a; &#xff08;1&#xff09;多位数问题、余数问题、年龄…

node.js服务器基础

node.js的事件循环 node.js是基于事件驱动的&#xff0c;通常在代码中注册想要等待的事件&#xff0c;设定好回调函数&#xff0c;当事件触发的时候就会调用回调函数。如果node.js没有要处理的事件了&#xff0c;那整个就结束了;事件里面可以继续插入事件&#xff0c;如果有事…

【2021】知识图谱导论(陈华钧)——阅读思考与笔记

tips&#xff1a;其中所有【】表示的内容为博主本人想法&#xff0c;非作者观点&#xff0c;请注意辨别。 这是一本全面覆盖知识图谱多个方面的书籍。书中不仅详细介绍了知识图谱的表示、存储、获取、推理、融合、问答和分析等七大方面&#xff0c;还深入探讨了多模态知识图谱…

【Nginx系列】Nginx启动失败

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

[⑦5G NR]: PSS/SSS同步信号学习

在5G中&#xff0c;PSS(Primary Synchronization Signal) 主同步信号和SSS(Secondary Synchronization Signal)辅同步信号是用于物理层的信号&#xff0c;用于小区的搜索。 PSS 跟据协议38.211 7.4.2.2章节&#xff0c;PSS是3条长度为127的m序列&#xff0c;分别对应 N I D (…

空间解析几何4-空间中线段到圆的距离【附MATLAB代码】

目录 理论公式 matlab代码 理论公式 对于解一元4次方程&#xff0c;请详见我的博客 一元四次方程求解 -【附MATLAB代码】-CSDN博客文章浏览阅读1.4k次&#xff0c;点赞41次&#xff0c;收藏4次。最近在研究机器人的干涉&#xff08;碰撞&#xff09;检测&#xff0c;遇到了一…

义堂镇韦家巷村第十六届老人节暨孝善互助基金启动仪式成功举行

金秋十月爽&#xff0c;浓浓敬老情。10月11日晚&#xff0c;以“孝善韦家巷情暖重阳节”为主题的兰山区义堂镇韦家巷村第十六届老人节暨韦家巷村孝善互助基金启动仪式在韦家巷村文化广场盛大举行。 山东省民间文艺家协会副主席、临沂市民间文艺家协会主席、临沂市文联办公室主…

使用git页面如何用旧项目创建一个新项目出来并且保留所有分支内容和提交历史

使用git页面如何用旧项目创建一个新项目出来并且保留所有分支内容和提交历史 1、点击创建项目 2、点击导入项目

antd table合并复杂单元格、分组合并行、分组合并列、动态渲染列、嵌套表头

项目里遇到个需求&#xff0c;涉及到比较复杂的单元格合并 、嵌套表头、分组合并行、合并列等&#xff0c;并且数据列还是动态的&#xff0c;效果图如下&#xff1a; 可以分组设置【显示列】例如&#xff1a;当前组为【合同约定】&#xff0c;显示列为【合同节点】和【节点金额…

Milvus向量数据库管理工具[Attu]实践

Attu是一款专为Milvus向量数据库打造的开源数据库管理工具&#xff0c;提供了便捷的图形化界面&#xff0c;极大地简化了对Milvus数据库的操作与管理流程。阿里云Milvus集成了Attu&#xff0c;以便更加高效地管理数据库、集合&#xff08;Collection&#xff09;、索引&#xf…

protobufJavascrip编码解码演示

protobuf&Javascrip编码解码演示 start 写一下 protobuf 相关知识记录在 python 环境和 js 环境中如何处理 protobuf。 1. protobuf是什么&#xff1f; 1.1 介绍 Protocol Buffers(简称Protobuf) &#xff0c;是Google出品的序列化框架&#xff0c;与开发语言无关&…

【Go】GO语言知识总结浅析

Go语言是一种现代化的编程语言&#xff0c;由Google于2007年设计并于2009年发布。它旨在使编程变得简单、高效&#xff0c;并且可以在多核处理器上轻松构建高性能应用。Go语言的编程思想、发展历史、版本特点、运行原理、数据类型、应用场景&#xff0c;以及在web开发、网络编程…

C语言内存分配

概要 C语言的内存存储是一个复杂但非常重要的主题&#xff0c;涉及到静态内存、动态内存、指针、堆栈等多种机制&#xff0c;明白了c语言的内存分配&#xff0c;可以帮助我们更好的编程和学习c语言。 一.内存分配的类型 在C/C中内存分为5个区&#xff0c;分别为栈区、堆区、…

上海大爷惹恼韭菜不敢现身了

国庆节前股市暴涨&#xff0c;立即掀起了一股“全民炒股”热浪&#xff0c;但天不遂人愿&#xff0c;有大量韭菜散户很快就被套牢。 这与满嘴跑火车的上海大爷网红“爱在深秋”&#xff0c;曾鼓吹“A股会涨到上万点”多少有些关联。10月9日股市暴跌后&#xff0c;就不见他人影…

MySQL进阶 - SQL优化

01 插入数据 【一】insert优化&#xff1a; ① 不要一行一行插入数据&#xff0c;直接批量插入多行数据(但一个insert语句不要插入超过一千条)。 insert into 表名 (字段列表) values (xx,xx...),(xx,xx...)...&#xff1b; ② 在进行插入操作时手动提交事务&#xff0c;避免频…

【代码】集合set

哈喽大家好&#xff0c;我是学霸小羊&#xff0c;今天来讲一讲集合&#xff08;set&#xff09;。 在数学上&#xff0c;集合长这样&#xff1a; 那今天就来讲一讲编程上的集合。 集合的定义&#xff1a;把一些元素按照某些规律放在一起&#xff0c;就形成了一个集合。比如说…