多线程-锁的种类

news2024/11/19 1:46:12

1 作用

Java中的锁主要用于保障多并发线程情况下数据的一致性。在多线程编程中为了保障数据的一致性,我们通常需要在使用对象或者方法之前加锁,这时如果有其他线程也需要使用该对象或者该方法,则首先要获得锁,如果某个线程发现锁正在被其他线程使用,就会进入阻塞队列等待锁的释放.直到其他线程执行完成并释放锁、该线程才有机会再次获取锁进行操作。这样就保障了在同一时刻只有一个线程持有该对象的锁并修改对象、从而保障数据的安全。

锁从乐观和悲观的角度可分为乐观锁和悲观锁,从获取资源的公平性角度可分为公平锁和非公平锁,从是否共享资源的角度可分为共享锁和独占锁,从锁的状态的角度可分为偏向锁、轻量级锁和重量级锁。同时,在JVM中还巧妙设计了自旋锁以更快地使用CPU资源。

 

2 乐观锁、悲观锁

  • 乐观锁

乐观锁采用乐观的思想处理数据,在每次读取数据时都认为别人不会修改该数据,所以不会上锁,但在更新时会判断在此期间别人有没有更新该数据、通常采用在写时先读出当前版本号然后加锁的方法。具体过程为:比较当前版本号与上一次的版本号,如果版本号一致.则更新,如果版本号不一致,则重复进行读、比较、写操作。

  • 悲观锁

悲观锁采用悲观思想处理数据,在每次读取数据时都认为别人会修改数据、所以每次在读写数据时都会上锁,这样别人想读写这个数据时就会阻塞、等待直到拿到锁。

Java 中的悲观锁大部分基于AQS(Abstract Qucued Synchronized,抽象的队列同步器)架构实现。AQS定义了一套多线程访问共享资源的同步框架,许多同步类的实现都依赖于它,例如常用的Synchronized、ReentrantLock、Semaphore、CountDownLatch等。该框架下的锁会先尝试以 CAS 乐观锁去获取锁、如果获取不到,则会转为悲观锁(如RetreenLock )。

比较适合写入操作比较频繁的场景,如果出现大量的读取操作,每次读取的时候都会进行加锁,这样会增加大量的锁的开销,降低了系统的吞吐量。 

3 公平锁、非公平锁

公平锁

概念: 是指多个线程按照申请锁的顺序来获取锁,线程直接进入队列中排队,队列中的第一个线程才能获得锁。

好处:公平锁的优点是等待锁的线程不会饿死。

缺点:是整体吞吐效率相对非公平锁要低,等待队列中除第一个线程以外的所有线程都会阻塞,CPU唤醒阻塞线程的开销比非公平锁大。

非公平锁

概念:非公平锁是多个线程加锁时直接尝试获取锁,获取不到才会到等待队列的队尾等待。但如果此时锁刚好可用,那么这个线程可以无需阻塞直接获取到锁,所以有可能出现后申请锁的线程先获取锁的场景

好处:非公平锁的优点是可以减少唤起线程的开销,整体的吞吐效率高,因为线程有几率不阻塞直接获得锁,CPU不必唤醒所有线程。

缺点:处于等待队列中的线程可能会饿死,或者等很久才会获得锁。

 

4 可重入锁、非可重入锁

可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者class),不会因为之前已经获取过还没释放而阻塞。Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。

 

5 共享锁、排它锁

共享锁和排它锁多用于数据库中的事物操作,主要针对读和写的操作。而在 Java 中,对这组概念通过 ReentrantReadWriteLock 进行了实现,它的理念和数据库中共享锁与排它锁的理念几乎一致,即一条线程进行读的时候,允许其他线程进入上锁的区域中进行读操作;当一条线程进行写操作的时候,不允许其他线程进入进行任何操作。即读 + 读可以存在,读 + 写、写 + 写均不允许存在。

共享锁:也称读锁或 S 锁。如果事务 T 对数据 A 加上共享锁后,则其他事务只能对 A 再加共享锁,不能加排它锁。获准共享锁的事务只能读数据,不能修改数据。

排它锁:也称独占锁、写锁或 X 锁。如果事务 T 对数据 A 加上排它锁后,则其他事务不能再对 A 加任何类型的锁。获得排它锁的事务即能读数据又能修改数据

 

6 自旋锁

  • 自旋锁认为:如果持有锁的线程能在很短的时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞、挂起状态,只需等一等(也叫作自旋),在等待持有锁的线程释放锁后即可立即获取锁,这样就避免了用户线程在内核状态的切换上导致的锁时间消耗。

  • 线程在自旋时会占用CPU,在线程长时间自旋获取不到锁时,将会产CPU 的浪费,甚至有时线程永远无法获取销而导致CPU 资源被永久占用,所以需要设定一个自旋等待的最大时间。在线程执行的时间超讨自旋等待的

  • 自旋锁的优缺点如下。

    • 优点:自旋锁可以减少CPU上下文的切换,对于占用锁的时间非常短或锁竞争不激烈的代码块来说性能大幅度提升,因为自旋的 CPU 耗时明显少于线程阻塞、挂起、再唤醒时两次CPU上下文切换所用的时间。

    • 缺点:在持有锁的线程占用锁时间过长或锁的竞争过于激烈时,线程在自旋过程中会长时间获取不到锁资源,将引起 CPU 的浪费。所以在系统中有复杂锁依赖的情况下不适合采用自旋锁。

  • 2.自旋锁的时间阈值

    • 自旋锁用于让当前线程占着CPU 的资源不释放,等到下次自旋获取锁资源后立即执行相关操作。但是如何选择自旋的执行时间呢?如果自旋的执行时间太长,则会有大量的线程处于自旋状态且占用CPU资源,造成系统资源浪费。因此、对自旋的周期选择将直接影响到系统的性能!

    • JDK的不同版本所采用的自旋周期不同,JDK 1.5为固定的时间,JDK 1.6引入了适应性自旋锁。适应性自旋锁的自旋时间不得是固定值,而是由上一次在同一个锁上的自旋时间及锁的拥有者的状态来决定的,可基本认为一个线程上下文切换的时间是就一个最佳时间。

7 重量级锁和轻量级锁

  • 重量级锁是基于操作系统的互斥量(Mutex Lock)而实现的锁,会导致进程在用户态与内核态之间切换,相对开销较大。

  • synchronized在内部基于监视器锁(Monitor)实现,监视器锁基于底层的操作系统的 Mutex Lock实现,因此 synchronized属于重量级锁。重量级锁需要在用户态和核心态之间做转换,所以synchronized的运行效率不高。

  • JDK在1.6版本以后,为了减少获取锁和释放锁所带来的性能消耗及提高性能,引人了轻量级锁和偏向锁。

  • 轻量级锁是相对于重量级锁而言的。轻量级锁的核心设计是在没有多线程竞争的前提下,减少重量级锁的使用以提高系统性能。轻量级锁适用于线程交替执行同步代码块的情况(即互斥操作),如果同一时刻有多个线程访问同一个锁,则将会导致轻量级锁膨胀为重量级锁。

  • 轻量级锁也叫自旋锁。

8 偏向锁

  • 除了在多线程之间存在竞争获取锁的情况,还会经常出现同一个锁被同一个线程多次获取的情况。偏向锁用于在某个线程获取某个锁之后,消除这个线程锁重人的开销,看起来似乎是这个线程得到了该锁的偏向(偏袒)。

  • 偏向锁的主要目的是在同一个线程多次获取某个锁的情况下尽量减少轻量级锁的执行路径、因为轻量级锁的获取及释放需要多次CAS ( Compare and Swap )原于操作,而偏向锁只需要在切换 ThreadID 时执行一次 CAS 原子操作,因此可以提高钡的运行效率。

  • 在出现多线程竞争锁的情况时,JVM 会自动撤销偏向锁,因此偏向锁的撤销操作的耗时必须少于节省下来的 CAS原子操作的耗时。

  • 综上所述,轻量级锁用于提高线程交替执行同步块时的性能,偏向锁则在某个线程交替执行同步块时进一步提高性能。

  • 锁的状态总共有4种:无锁、偏向锁、轻量级锁和重量级锁。随着锁竞争越来越激烈,锁可能从偏向锁升级到轻量级锁,再升级到重量级锁,但在Java 中锁只单向升级,不会降级。

  • hashtable

9 分段锁

  • 分段锁并非一种实际的锁,而是一种思想,用于将数据分段并在每个分段上都单独加锁,把锁进一步细粒度化,以提高并发效率。ConcurrentHashMap在内部就是使用分段锁实现的。

9 CAS

CAS:Compare and Swap,即比较再交换。

dk5增加了并发包java.util.concurrent.*,其下面的类使用CAS算法实现了区别于synchronouse同步锁的一种乐观锁。JDK 5之前Java语言是靠synchronized关键字保证同步的,这是一种独占锁,也是是悲观锁。

对CAS的理解,CAS是一种无锁算法,CAS有3个操作数,要更新的变量V,旧的预期值E,要修改的新值N。当且仅当V和E相同的情况下,将内存值V修改为N,否则什么都不做。

10 ABA问题

ABA问题是指在CAS操作时,其他线程将变量值A改为了B,但是又被改回了A,等到本线程使用期望值A与当前变量进行比较时,发现变量A没有变,于是CAS就将A值进行了交换操作,但是实际上该值已经被其他线程改变过。 解决办法: 在变量前面加上版本号,每次变量更新的时候变量的版本号都+1,即A->B->A就变成了1A->2B->3A。只要变量被某一线程修改过,变量对应的版本号就会发生递增变化,从而解决了ABA问题。

11 锁升级

没有优化以前,synchronized是重量级锁(悲观锁),使用 wait 和 notify、notifyAll 来切换线程状态非常消耗系统资源;线程的挂起和唤醒间隔很短暂,这样很浪费资源,影响性能。所以 JVM 对 synchronized 关键字进行了优化,把锁分为 无锁、偏向锁、轻量级锁、重量级锁 状态。 锁的级别从低到高依次为:无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级。

 

1、无锁: 没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功,其他修改失败的线程会不断重试直到修改成功。

2、偏向锁: 偏向锁的核心思想就是锁会偏向第一个获取它的线程,该线程是不会主动释放偏向锁的,只有当其他线程尝试竞争偏向锁才会被释放。在接下来的执行过程中该锁没有其他的线程获取,则持有偏向锁的线程永远不需要再进行同步。

当一个线程访问同步块并获取锁的时候,会在对象头和栈帧中的锁记录里存储偏向的线程 ID,以后该线程在进入和退出同步块时不需要进行 CAS 操作来加锁和解锁,只需要检查当前 Mark Word 中存储的线程是否为当前线程,如果是,则表示已经获得对象锁;否则,需要测试 Mark Word 中偏向锁的标志是否为1,如果没有则使用 CAS 操作竞争锁,如果设置了,则尝试使用 CAS 将对象头的偏向锁指向当前线程。

偏向锁的撤销,需要在某个时间点上没有字节码正在执行时,先暂停拥有偏向锁的线程,然后检查持有偏向锁的线程是否活着。如果线程不处于活动状态,则将对象头设置成无锁状态,并撤销偏向锁;如果线程处于活动状态,升级为轻量级锁的状态。

3、轻量级锁: 轻量级锁是指当锁是偏向锁的时候,被第二个线程 B 所访问,此时偏向锁就会升级为轻量级锁,线程 B 会通过自旋的形式尝试获取锁,线程不会阻塞,从而提高性能。

当前只有一个等待线程,则该线程将通过自旋进行等待。但是当自旋超过一定的次数时,轻量级锁便会升级为重量级锁;当一个线程已持有锁,另一个线程在自旋,而此时又有第三个线程来访时,轻量级锁也会升级为重量级锁。

4、重量级锁: 指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。

重量级锁将程序运行交出控制权,将线程挂起,由操作系统来负责线程间的调度,负责线程的阻塞和执行。这样会出现频繁地对线程运行状态的切换,线程的挂起和唤醒,消耗大量的系统资源,导致性能低下。

重量级锁通过对象内部的监视器(monitor)实现,而其中 monitor 的本质是依赖于底层操作系统的 Mutex Lock 实现,操作系统实现线程之间的切换需要从用户态切换到内核态,切换成本非常高。

 

总结:锁的升级过程,如果只有一个线程获取到锁,这个锁就是偏向锁,因为对象头和栈帧中的锁记录里存储偏向的线程 ID,如果没有别的线程来竞争锁,那么直接执行代码;如果有别的线程在竞争锁,那么会释放偏向锁,线程会通过自旋的方式尝试获取锁,这个阶段的锁叫自旋锁(轻量级锁),如果经过多次自旋还是没有获取到锁,那么就会变成重量级锁。

原因:

假设有两个线程t1,t2

如果t1获取到锁以后,1ms以后就释放锁了,这时候用轻量级锁会更好一点,因为自旋锁不会释放资源,因为线程进入阻塞,就绪,CPU调度,竞争锁资源都是需要时间的,这中间的时间可能要几十毫秒,效率会比较低。

如果t1获取到锁以后,60s以后才释放锁,如果这时候t2一直处于自旋状态,自旋状态是不会释放锁的,一直占用cpu资源。

12 锁升级代码

(1) java对象组成

 

2 代码

新建maven项目,导入依赖

 <dependency>
     <groupId>org.openjdk.jol</groupId>
     <artifactId>jol-core</artifactId>
     <version>0.9</version>
</dependency>

 main方法中测试

import org.openjdk.jol.info.ClassLayout;

public class Test {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(ClassLayout.parseInstance(a).toPrintable());
    }
}
class A{
    public int a = 4;
    public long b = 5L;
    public boolean c = true;
}

 结果:

 

3 mark word

mark word占用了8个字节(64位), markword的结构,定义在markOop.hpp文件:

 

4 查看hashcode

代码中添加

System.out.println(Integer.toHexString(a.hashCode()));

 

4 查看偏向锁

BiasedLockingStartupDelay表示系统启动几秒钟后启用偏向锁。默认为4秒,原因在于,系统刚启动时,一般数据竞争是比较激烈的,此时启用偏向锁会降低性能。由于这里为了测试偏向锁的性能,所以把延迟偏向锁的时间设置为0

JVM参数设置: -XX:BiasedLockingStartupDelay=0

代码:

 

public class Test {
    public static void main(String[] args) {
        A a = new A();
//        System.out.println(Integer.toHexString(a.hashCode()));
        System.out.println(ClassLayout.parseInstance(a).toPrintable());

        synchronized (a){
            System.out.println(ClassLayout.parseInstance(a).toPrintable());
        }

        synchronized (a){
            System.out.println(ClassLayout.parseInstance(a).toPrintable());
        }
    }
}
class A{
    public int a = 4;
    public long b = 5L;
    public boolean c = true;
}

 

5 查看轻量级锁

 

package com.yang.test;

import org.openjdk.jol.info.ClassLayout;

public class Test {
    public static void main(String[] args) {
        A a = new A();
//        System.out.println(Integer.toHexString(a.hashCode()));
        System.out.println(ClassLayout.parseInstance(a).toPrintable());

        synchronized (a){
            System.out.println(ClassLayout.parseInstance(a).toPrintable());
        }

        synchronized (a){
            System.out.println(ClassLayout.parseInstance(a).toPrintable());
        }

        new Thread(()->{
            synchronized (a){
                System.out.println(ClassLayout.parseInstance(a).toPrintable());
            }
        }).start();
    }
}
class A{
    public int a = 4;
    public long b = 5L;
    public boolean c = true;
}

 

6 重量级锁

package com.yang.test;

import org.openjdk.jol.info.ClassLayout;

public class Test2 {

    private static Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        // 去掉偏向锁延时操作
        System.out.println(ClassLayout.parseInstance(lock).toPrintable());  //无锁

        new Thread(()->{
            System.out.println("子线程获取锁之前打印对象头信息");
            System.out.println(ClassLayout.parseInstance(lock).toPrintable());  //无锁

            synchronized (lock){
                try {
                    System.out.println("子线程获取到锁打印对象头信息");
                    System.out.println(ClassLayout.parseInstance(lock).toPrintable());
                    Thread.sleep(5000);
                    System.out.println("---子线程----");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.println("子线程释放锁打印对象头信息");
            System.out.println(ClassLayout.parseInstance(lock).toPrintable());
        },"子线程").start();
        Thread.sleep(1000);
        sync();
    }
    public static void sync(){
        synchronized (lock){
            System.out.println("主线程获取到锁打印对象头信息");
            System.out.println(ClassLayout.parseInstance(lock).toPrintable());
        }

    }
}

关于线程id和hashcode的问题

HotSpot VM的锁实现机制是:

当一个对象已经计算过identity hash code,它就无法进入偏向锁状态; 当一个对象当前正处于偏向锁状态,并且需要计算其identity hash code的话,则它的偏向锁会被撤销,并且锁会膨胀为轻量级锁或者重量锁; 轻量级锁的实现中,会通过线程栈帧的锁记录存储Displaced Mark Word;重量锁的实现中,ObjectMonitor类里有字段可以记录非加锁状态下的mark word,其中可以存储identity hash code的值。

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

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

相关文章

案例研究|农业信息化企业天演维真的堡垒机选型思路与落地实践

浙江天演维真网络科技股份有限公司&#xff08;以下简称为“天演维真”&#xff09;成立于2004年&#xff0c;是中国领先的乡村振兴数字化服务整体解决方案提供商。作为中国品牌农业信息化服务的先行者、中国农产品数字身份识别技术开创者&#xff0c;天演维真的产品已助力全国…

【Python】Python基础语法

总感慨万千&#xff0c;虽只道寻常 文章目录 前言1. python与Java的主要区别2. 数据类型3. 输入与输出3.1 输入3.2 输出 4. 注释5. 运算符6. 条件语句7. 循环8. 函数9. 列表9.1 创建9.2 根据下标访问元素9.3 列表切片9.4 遍历9.5 插入元素9.6 查找元素下标9.7 删除元素9.8 列表…

Spring Boot - Junit4 / Junit5 / Spring Boot / IDEA 关系梳理

文章目录 PreJunit4 / Junit5 / Spring Boot / IDEAIDEA版本Spring-Boot-Older-Release-NotesSpringBootTest 起源 & Spring-Boot-1.4-Release-Notes2.0.0.RELEASE ----- 2.0.9.RELEASE2.1.0.RELEASE ----- 2.1.18.RELEASE2.2.0.RELEASE ~ 2.2.13.RELEASE2.3.0.RELEASE ~ 2…

记一次线程堵塞(挂起)导致消息队列积压

1 背景 A服务作为生产者&#xff0c;每天发送上千万的mq消息&#xff0c;每一个消息包含500个用户ids数据。B服务作为消费者&#xff0c;接受MQ消息并通过http调用第三方请求进行业务处理&#xff0c;消费组启用了rabbitmq的多线程消费组&#xff0c;一个实例并发40个mq消费者…

索尼mp4变成rsv修复案例(ILME-FX3)

索尼mp4的修复案例讲过很多&#xff0c;这次是索尼的ILME-FX3也算是一个畅销的机型&#xff0c;一般索尼没有封装的文件是RSV文件&#xff0c;但是极少遇到有多个RSV文件的&#xff0c;下边我们来讲下这个特殊案例。 故障文件:4个RSV文件&#xff0c;大小在1.78G~28G多 故障现…

(测评补单)Lazada、Shopee、Zalora:探索东南亚电商市场

随着互联网的发展&#xff0c;电子商务在东南亚地区迅速崛起。在这个充满活力和潜力的市场中&#xff0c;Lazada、Shopee和Zalora成为了三大领先的电商平台。它们以其独特的商业模式和创新的服务&#xff0c;吸引了数百万用户。本文将深入探讨这三家电商巨头的发展历程、核心竞…

一文讲透机器学习超参数调优!

公众号&#xff1a;尤而小屋作者&#xff1a;Peter编辑&#xff1a;Peter 大家好&#xff0c;我是Peter~ 本文的主题&#xff1a;机器学习建模的超参数调优。开局一张图&#xff1a; 文章很长&#xff0c;建议直接收藏~ 一、什么是机器学习超参数&#xff1f; 机器学习超参数…

代码随想录训练营 打家劫舍

代码随想录训练营 dp 198. 打家劫舍&#x1f338;code 213. 打家劫舍 II&#x1f338;分析code 337.打家劫舍 III&#x1f338; 198. 打家劫舍&#x1f338; 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素…

FastChat工作原理解析

在了解FastChat如何完成大模型部署前&#xff0c;先了解下Huggingface提供的Transformer库。Hugggingface提供的Transformer库 Hugging Face 的 Transformers 库是一个用于自然语言处理&#xff08;NLP&#xff09;任务的 Python 库&#xff0c;旨在简化和加速使用预训练语言模…

Vue3 中的几个坑,你都见过吗?

Vue3 目前已经趋于稳定&#xff0c;不少代码库都已经开始使用它&#xff0c;很多项目未来也必然要迁移至 Vue3。本文记录我在使用 Vue3 时遇到的一些问题&#xff0c;希望能为其他开发者提供帮助。 1. 使用 reactive 封装基础数据类型 传统开发模式中&#xff0c;数据声明很简…

[论文阅读]Visual Attention Network原文翻译

[论文链接]https://arxiv.org/abs/2202.09741 摘要 虽然一开始是被设计用于自然语言处理任务的&#xff0c;但是自注意力机制在多个计算机视觉领域掀起了风暴。然而&#xff0c;图像的二维特性给自注意力用于计算机视觉带来了三个挑战。&#xff08;1&#xff09;将图像视作一…

怎样获取字符串数组的长度_使用sizeof(array) / sizeof(array[0])

使用sizeof() C、C中没有提供直接获取数组长度的函数&#xff0c;对于存放字符串的字符数组提供了一个strlen函数获取长度&#xff0c;那么对于其他类型的数组如何获取他们的长度呢&#xff1f; 其中一种方法是使用sizeof(array) / sizeof(array[0]), 在C语言中习惯上在使用时…

Unity 性能优化之Shader分析处理函数ShaderUtil.HasProceduralInstancing: 深入解析与实用案例

Unity 性能优化之Shader分析处理函数ShaderUtil.HasProceduralInstancing: 深入解析与实用案例 点击封面跳转到Unity国际版下载页面 简介 在Unity中&#xff0c;性能优化是游戏开发过程中非常重要的一环。其中&#xff0c;Shader的优化对于游戏的性能提升起着至关重要的作用。…

redis缓存详解

一、Redisson分布式锁存在问题 1、基于redis实现的分布式锁&#xff0c;如果redis集群出现master宕机&#xff0c;而从节点没有接收到锁对应的key&#xff0c;被选举成新的master就可能存在被其它线程加锁成功则存在加锁问题 2、 基于上面的问题&#xff0c;可以把redis分为多…

SpringBoot国际化配置组件支持本地配置和数据库配置

文章目录 0. 前言i18n-spring-boot-starter1. 使用方式0.引入依赖1.配置项2.初始化国际化配置表3.如何使用 2. 核心源码实现一个拦截器I18nInterceptorI18nMessageResource 加载国际化配置 3.源码地址 0. 前言 写个了原生的SpringBoot国际化配置组件支持本地配置和数据库配置 背…

口袋参谋:99.99%商家都学的防骗技巧!

​99%的淘宝天猫商家&#xff0c;必然都要解决一个问题&#xff01;&#xff01;&#xff01; 如何让自己不被敲诈勒索且骗钱&#xff01; 直接看真实案例 看这个骗子&#xff0c;是如何赤裸裸诈骗商家的&#xff01; 如果你不想再当冤大头&#xff0c;告诉你一个99.99%有效…

SSM - Springboot - MyBatis-Plus 全栈体系(六)

第二章 SpringFramework 四、SpringIoC 实践和应用 3. 基于 注解 方式管理 Bean 3.1 实验一&#xff1a;Bean 注解标记和扫描 (IoC) 3.1.1 注解理解 和 XML 配置文件一样&#xff0c;注解本身并不能执行&#xff0c;注解本身仅仅只是做一个标记&#xff0c;具体的功能是框…

分类预测 | MATLAB实现基于SVM-Adaboost支持向量机结合AdaBoost多输入分类预测

分类预测 | MATLAB实现基于SVM-Adaboost支持向量机结合AdaBoost多输入分类预测 目录 分类预测 | MATLAB实现基于SVM-Adaboost支持向量机结合AdaBoost多输入分类预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.MATLAB实现基于SVM-Adaboost支持向量机结合Ada…

Python 图形化界面基础篇:理解 Tkinter 主事件循环

Python 图形化界面基础篇&#xff1a;理解 Tkinter 主事件循环 引言什么是 Tkinter 主事件循环&#xff1f; Tkinter 主事件循环的使用步骤1&#xff1a;导入 Tkinter 模块步骤2&#xff1a;创建 Tkinter 窗口对象步骤3&#xff1a;设置窗口标题和添加 GUI 元素步骤4&#xff1…

router-link 和 router-view的区别

router-link 实现路由之间的跳转 router-view&#xff08;路由出口组件 -> 渲染路径匹配到的视图组件&#xff09; 当你访问的地址与路由path相符时&#xff0c;会将指定的组件替换该router-view router-link router-link 点击实现路由跳转&#xff0c;to属性指向目标地址&…