CAS原理与JUC原子类详解

news2024/11/22 23:51:23

CAS原理与JUC原子类

由于JVM的synchronized重量级锁涉及操作系统(如Linux)内核态下的互斥锁的使用,其线程阻塞和唤醒都涉及进程在用户态和到内核态的频繁切换,导致重量级锁开销大、性能低。而JVM的synchronized轻量级锁使用CAS(Compare And Swap,比较并交换)进行自旋抢锁,CAS是CPU指令级的原子操作并处于用户态下,所以JVM轻量级锁开销较小。

什么是 CAS

JDK 5所增加的JUC(java.util.concurrent)并发包对操作系统的底层CAS原子操作进行了封装,为上层Java程序提供了CAS操作的API。

Unsafe 类中的 CAS 方法

Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别、不安全的底层操作,如直接访问系统内存资源、自主管理内存资源等,Unsafe大量的方法都是原生(native)方法,基于C++语言实现,这些方法在提升Java运行效率、增强Java语言底层资源操作能力方面起到了很大的作用。
Unsafe类从名字中我们可以看出这个类对普通程序员来说是“危险”的,一般的应用开发都不会涉及此类,Java官方也不建议直接在应用程序中使用。为什么此类取名为Unsafe呢?由于使用Unsafe类可以像C语言一样使用指针操作内存空间,这无疑增加了指针相关问题、内存泄漏问题的出现概率。总之,在程序中过度使用Unsafe类会使得程序出错的概率变大,使得安全的语言Java变得不再“安全”,因此对
Unsafe的使用一定要慎重。
操作系统层面的CAS是一条CPU的原子指令(cmpxchg指令),正是由于该指令具备了原子性,因此使用CAS操作数据时不会造成数据不一致的问题,Unsafe提供的CAS方法直接通过native方式(封装C++代码)调用了底层的CPU指令cmpxchg。
完成Java应用层的CAS操作主要涉及的Unsafe方法调用,具体如下:

  1. 获取Unsafe实例。
  2. 调用Unsafe提供的CAS方法,这些方法主要封装了底层CPU的CAS原子操作。
  3. 调用Unsafe提供的字段偏移量方法,这些方法用于获取对象中的字段(属性)偏移量,此偏移量值需要作为参数提供给CAS操作。

获取 Unsafe 实例
Unsafe类是一个final修饰的不允许继承的最终类,而且其构造函数是private类型的方法。因此,我们无法在外部对Unsafe进行实例化,只能通过反射的方式自定义的获取Unsafe实例的辅助方法。
Unsafe提供的CAS方法包含4个操作数——字段所处的对象、字段内存位置、预期原值及新值。在执行Unsafe的CAS方法时,这些方法首先将内存位置的值与预期值(旧的值)比较,如果相匹配,那么处理器会自动将该内存位置的值更新为新值,并返回true;如果不匹配,处理器不做任何操作,并返回false。
Unsafe的CAS操作会将第一个参数(对象的指针、地址)与第二个参数(字段偏移量)组合在一起,计算出最终的内存操作地址。

使用 CAS 进行无锁编程

CAS是一种无锁算法,该算法关键依赖两个值——期望值(就值)和新值,底层CPU利用原子操作判断内存原值与期望值是否相等,如果相等就给内存地址赋新值,否则不做任何操作。
使用CAS进行无锁编程的步骤大致如下:
1)获得字段的期望值(oldValue)。
2)计算出需要替换的新值(newValue)。
3)通过CAS将新值(newValue)放在字段的内存地址上,如果CAS失败就重复第1步到第2步,直到CAS成功,这种重复俗称CAS自旋。

当CAS内存地址的值与预期值比较时,如果相等,就证明内存地址的值没有被修改,可以替换成新值,然后继续往下运行;如果不相等,就说明内存地址的值已经被修改,放弃替换操作,然后重新自旋。当并发修改的线程少,冲突出现的机会少时,自旋的次数也会很少,CAS性能会很高;当并发修改的线程多,冲突出现的机会多时,自旋的次数也会很多,CAS性能会大大降低。所以,提升CAS无锁编程效率的关键在于减少冲突的机会。

JUC 原子类

在多线程并发执行时,诸如“++”或“–”类的运算不具备原子性,不是线程安全的操作。通常情况下,大家会使用synchronized将这些线程不安全的操作变成同步操作,但是这样会降低并发程序的性能。所以,JDK为这些类型不安全的操作提供了一些原子类,与synchronized同步机制相比,JDK原子类基于CAS轻量级原子操作实现,使得程序运行效率变得更高。

JUC 中的 Atomic 原子操作包

Atomic操作翻译成中文是指一个不可中断的操作,即使在多个线程一起执行Atomic类型操作的时候,一个操作一旦开始,就不会被其他线程中断。所谓Atomic类,指的是具有原子操作特征的类。
JUC并发包中原子类都存放在java.util.concurrent.atomic类路径下,根据操作的目标数据类型,可以将JUC包中的原子类分为4类:基本原子类、数组原子类、原子引用类和字段更新原子类。

  1. 基本原子类
    基本原子类的功能是通过原子方式更新Java基础类型变量的值。基本原子类主要包括以下三个:

    • AtomicInteger:整型原子类。
    • AtomicLong:长整型原子类。
    • AtomicBoolean:布尔型原子类。
  2. 数组原子类
    数组原子类的功能是通过原子方式更新数组中的某个元素的值。数组原子类主要包括了以下三个:

    • AtomicIntegerArray:整型数组原子类。
    • AtomicLongArray:长整型数组原子类。
    • AtomicReferenceArray:引用类型数组原子类。
  3. 引用原子类
    引用原子类主要包括以下三个:

    • AtomicReference:引用类型原子类。

    • AtomicMarkableReference:带有更新标记位的原子引用类型。

      AtomicMarkableReference类将boolean标记与引用关联起来,可以解决使用AtomicBoolean进行原子方式的更新时可能出现的ABA问题。

    • AtomicStampedReference:带有更新版本号的原子引用类型。

      AtomicStampedReference类将整数值与引用关联起来,可以解决使用AtomicInteger进行原子方式的更新时可能出现的ABA问题。

  4. 字段更新原子类
    字段更新原子类主要包括以下三个:

    • AtomicIntegerFieldUpdater:原子更新整型字段的更新器。
    • AtomicLongFieldUpdater:原子更新长整型字段的更新器。
    • AtomicReferenceFieldUpdater:原子更新引用类型里的字段。

基础原子类 AtomicInteger

基础原子类AtomicInteger常用的方法主要如下:

public final int get()
//获取当前的值
public final int getAndSet(int newValue)
//获取当前的值,然后设置新的值
public final int getAndIncrement()
//获取当前的值,然后自增
public final int getAndDecrement()
//获取当前的值,然后自减
public final int getAndAdd(int delta)
//获取当前的值,并加上预期的值
boolean compareAndSet(int expect, int update)
//通过CAS方式设置整数值

在多线程环境下,如果涉及基本数据类型的并发操作,不建议采用synchronized重量级锁进行线程同步,而是建议优先使用基础原子类保障并发操作的线程安全性。

AtomicInteger 线程安全原理

基础原子类(以AtomicInteger为例)主要通过CAS自旋+volatile相结合的方案实现,既保障了变量操作的线程安全性,又避免了synchronized重量级锁的高开销,使得Java程序的执行效率大为提升。CAS用于保障变量操作的原子性,volatile关键字用于保障变量的可见性,二者常常结合使用。
下面以AtomicInteger源码为例分析一下原子类的CAS自旋 + volatile相结合的实现方案。AtomicInteger源码的具体代码如下:

public class AtomicInteger extends Number implements java.io.Serializable {
          //Unsafe类实例
          private static final Unsafe unsafe = Unsafe.getUnsafe();
          //内部value值,使用volatile保证线程可见性
          private volatile int value;
          //value属性值的地址偏移量
          private static final long valueOffset;
          static {
          	try {
          		//计算value属性值的地址偏移量
          		valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
          	} catch (Exception ex) { throw new Error(ex); }
          }
          //初始化
          public AtomicInteger(int initialValue) {
          	value = initialValue;
          }
          //获取当前value值
          public final int get() {
          	return value;
          }
    
    	  //方法:封装底层的CAS操作,对比expect(期望值)与value,若不同则返回false
          //若expect与value相同,则将新值赋给value,并返回true
          public final boolean compareAndSet(int expect, int update) {
          	return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
          }
    
          //方法:返回旧值并赋新值
          public final int getAndSet(int newValue) {
          	for (;;) {
          		//自旋
          		int current = get();
          		//获取旧值
          		//以CAS方式赋值,直到成功返回
          		if (compareAndSet(current, newValue)) return current;
         	}
          }//还有setAndGet方法是返回新值
          
          //方法:安全自增i++
          public final int getAndIncrement() {
          	for (;;) {
         		//自旋
          		int current = get();
         	 	int next = current + 1;
          		if (compareAndSet(current, next)) return current;
          	}
          }
          //方法:自定义增量数
          public final int getAndAdd(int delta) {
          	for (;;) {
          		//自旋
          		int current = get();
          		int next = current + delta;
          		if (compareAndSet(current, next)) return current;
          	}
          }
}

AtomicInteger源码中的主要方法都是通过CAS自旋实现的。CAS自旋的主要操作为:如果一次CAS操作失败,获取最新的value值后,再次进行CAS操作,直到成功。另外,AtomicInteger所包装的内部value成员是一个使用关键字volatile修饰的内部成员。关键字volatile的原理比较复杂,简单地说,该关键字可以保证任何线程在任何时刻总能拿到该变量的最新值,其目的在于保障变量值的线程可见性

对象操作的原子性

基础的原子类型只能保证一个变量的原子操作,当需要对多个变量进行操作时,CAS无法保证原子性操作,这时可以用AtomicReference(原子引用类型)保证对象引用的原子性。简单来说,如果需要同时保障对多个变量操作的原子性,就可以把多个变量放在一个对象中
进行操作。与对象操作的原子性有关的原子类型,除了引用类型原子类之外,还包括属性更新原子类。

引用类型原子类

引用类型原子类包括以下3种:

  • AtomicReference:基础的引用原子类。

  • AtomicStampedReference:带印戳的引用原子类。

  • AtomicMarkableReference:带修改标志的引用原子类。

属性更新原子类

如果需要保障对象某个字段(或者属性)更新操作的原子性,需要用到属性更新原子类。属性更新原子类有以下三个:
AtomicIntegerFieldUpdater:保障整型字段的更新操作的原子性。
AtomicLongFieldUpdater:保障长整型字段的更新操作的原子性。
AtomicReferenceFieldUpdater:保障引用字段的更新操作的原子性。

使用属性更新原子类保障属性安全更新的流程大致需要两步:

  1. 更新的对象属性必须使用public volatile修饰符。

  2. 因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。

ABA 问题

由于CAS原子操作性能高,因此其在JUC包中被广泛应用,只不过如果使用得不合理,CAS原子操作会存在ABA问题。
ABA问题举一个例子来说明:比如一个线程A从内存位置M中取出V1,另一个线程B也取出V1。现在假设线程B进行了一些操作之后将M位置的数据V1变成了V2,然后又在一些操作之后将V2变成V1。之后,线程A进行CAS操作,但是线程A发现M位置的数据仍然是V1,最后线程
A操作成功。尽管线程A的CAS操作成功,但是不代表这个过程是没有问题的,线程A操作的数据V1可能已经不是之前的V1,而是被线程B替换过的V1,这就是ABA问题。比如队列中的元素,很有可能元素没变,但是它的next已经变了,那么就不再是原来的那个链表了。

ABA 问题解决方案

很多乐观锁的实现版本都是使用版本号(Version)方式来解决ABA问题。乐观锁每次在执行数据的修改操作时都会带上一个版本号,版本号和数据的版本号一致就可以执行修改操作并对版本号执行加1操作,否则执行失败。因为每次操作的版本号都会随之增加,所以不会出现ABA问题,因为版本号只会增加不会减少。

使用 AtomicStampedReference 解决 ABA 问题

参考乐观锁的版本号,JDK提供了一个类似AtomicStampedReference类来解决ABA问题。AtomicStampReference在CAS的基础上增加了一个Stamp(印戳或标记),使用这个印戳可以用来觉察数据是否发生变化,给数据带上了一种实效性的检验。
AtomicStampReference的compareAndSet()方法首先检查当前的对象引用值是否等于预期引用,并且当前印戳标志是否等于预期标志,如果全部相等,就以原子方式将引用值和印戳标志的值更新为给定的更新值。

使用 AtomicMarkableReference 解决 ABA 问题

AtomicMarkableReference是AtomicStampedReference的简化版,不关心修改过几次,仅仅关心是否修改过。因此,其标记属性mark是boolean类型,而不是数字类型,标记属性mark仅记录值是否修改过。AtomicMarkableReference适用于只要知道对象是否被修改过的场景,而不适用于对象被反复修改的场景。

提升高并发场景下 CAS 操作的性能

在争用激烈的场景下,会导致大量的CAS空自旋。比如,在大量的线程同时并发修改一个AtomicInteger时,可能有很多线程会不停地自旋,甚至有的线程会进入一个无限重复的循环中。大量的CAS空自旋会浪费大量的CPU资源,大大降低了程序的性能。除了存在CAS空自旋之外,在SMP架构的CPU平台上,大量的CAS操作还可能导致“总线风暴”。
在高并发场景下如何提升CAS操作性能呢?可以使用LongAdder替代AtomicInteger,采用以空间换时间的思想。Java 8提供一个新的类LongAdder,以空间换时间的方式提升高并发场景下CAS操作性能。LongAdder核心思想就是热点分离,与ConcurrentHashMap的设计思想类似:将value值分离成一个数组,当多线程访问时,通过Hash算法将线程映射到数组的一个元素进行操作;而获取最终的value结果时,则将数组的元素求和。最终,通过LongAdder将内部操作对象从单个value值“演变”成一系列的数组元素,从而减小了内部竞争的粒度。LongAdder的演变如图所示:
image-20230824185731994

LongAdder 的原理

AtomicLong使用内部变量value保存着实际的long值,所有的操作都是针对该value变量进行。也就是说,在高并发环境下,value变量其实是一个热点,也就是N个线程竞争一个热点。重试线程越多,就意味着CAS的失败概率越高,从而进入恶性CAS空自旋状态。LongAdder的基本思路就是分散热点,将value值分散到一个数组中,不同线程会命中到数组的不同槽(元素)中,各个线程只对自己槽中的那个值进行CAS操作。这样热点就被分散了,冲突的概率就小很多。使用LongAdder,即使线程数再多也不担心,各个线程会分配到多个元素上去更新,增加元素个数就可以降低 value的“热度”,AtomicLong中的恶性CAS空自旋就解决了。如果要获得完整的LongAdder存储的值,只要将各个槽中的变量值累加,返回最终的累加之后的值即可。
LongAdder的实现思路与ConcurrentHashMap中分段锁基本原理非常相似,本质上都是不同的线程在不同的单元上进行操作,这样减少了线程竞争,提高了并发效率。LongAdder的设计体现了空间换时间的思想,不过在实际高并发场景下,数组元素所消耗的空间可以忽略不计。

LongAdder 实例的内部结构具体如图所示:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
LongAdder的内部成员包含一个base值和一个cells数组。在最初无竞争时,只操作base的值;当线程执行CAS失败后,才初始cells数组,并为线程分配所对应的元素。

CAS 在 JDK 中的广泛应用

CAS的优势主要有两点:

  1. 属于无锁编程,线程不存在阻塞和唤醒这些重量级的操作。
  2. 进程不存在用户态和内核态之间的运行切换,进程不需要承担频繁切换的开销。

CAS 操作的弊端

CAS操作的弊端主要有以下4点。

  1. ABA问题
    使用CAS操作内存数据时,当数据发生过变化也能更新成功,如操作序列A==>B==>A时,最后一个CAS的预期数据A实际已经发生过更改,但也能更新成功,这就产生了ABA问题。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候将版本号加1,那么操作序列A==>B==>A的就会变成A1==>B2==>A3,如果将A1当作A3的预期数据,就会操作失败。
    JDK提供了两个类AtomicStampedReference和AtomicMarkableReference来解决ABA问题。比较常用的是AtomicStampedReference类,该类的compareAndSet方法的作用是首先检查当前引用是否等于预期引用,以及当前印戳是否等于预期印戳,如果全部相等,就以原子方式将引用和印戳的值一同设置为新的值。
  2. 只能保证一个共享变量之间的原子性操作
    当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,CAS无法保证操作的原子性。
    一个比较简单的规避方法为:把多个共享变量合并成一个共享变量来操作。JDK提供了AtomicReference类来保证引用对象之间的原子性,可以把多个变量放在一个AtomicReference实例后再进行CAS操作。比如有两个共享变量i=1、j=2,可以将二者合并成一个
    对象,然后用CAS来操作该合并对象的AtomicReference引用。
  3. 无效CAS会带来开销问题
    自旋CAS如果长时间不成功(不成功就一直循环执行,直到成功为止),就会给CPU带来非常大的执行开销。
  4. 在部分CPU平台上存在“总线风暴”问题
    CAS操作和volatile一样也需要CPU进行通过MESI协议各个内核的“Cache一致性”,会通过CPU的BUS(总线)发送大量MESI协议相关的消息,产生“Cache一致性流量”。因为总线被设计为固定的“通信能力”,如果Cache一致性流量过大,总线将成为瓶颈,这就是所谓的“总线风暴”。

提升 CAS 性能

提升CAS性能有效方式之一是以空间换时间,分散竞争热点。较为常见的方案为:

  1. 分散操作热点,使用LongAdder替代基础原子类AtomicLong,LongAdder将单个CAS热点(value值)分散到一个cells数组中。
  2. 使用队列削峰,将发生CAS争用的线程加入一个队列中排队,降低CAS争用的激烈程度。JUC中非常重要的基础类AQS(抽象队列同步器)就是这么做的。比如大量线程如果都用CAS抢锁那么就会大量重复自旋CAS,但是通过队列让具有抢锁能力的线程才CAS就可以降低竞争程度,避免大量自旋消耗CPU性能。

提升CAS性能有效方式之二是使用线程本地变量,从根本上避免竞争。

CAS 操作在 JDK 中的应用

CAS在java.util.concurrent.atomic包中的原子类、Java AQS及其显式锁、CurrentHashMap等重要并发容器类的实现都有非常广泛的应用。在java.util.concurrent.atomic包的原子类(如AtomicXXX中)都使用了CAS保障对数字成员进行操作的原子性。java.util.concurrent的大多数类(包括显式锁、并发容器)都是基于AQS和AtomicXXX实现的,其中AQS通过CAS保障其内部双向队列队头、队尾操作的原子性。


感谢耐心看到这里的同学,觉得文章对您有帮助的话希望同学们不要吝啬您手中的赞,动动您智慧的小手,您的认可就是我创作的动力!
之后还会勤更自己的学习笔记,感兴趣的朋友点点关注哦。

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

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

相关文章

xml的语法

<!-- 1、每一个xml,有且只有一个根标签&#xff0c;所有xml标签必须写在根标签中 2、标签必须要有合闭 3、xml格式是否正确&#xff0c;可以通过浏览器打开xml。来校验xml格式是否正确 4、xml是区别大小写的 5、xml书写标签名时&#xff0c;不要出现空格等特…

JSKarel教学编程机器人使用介绍

JSKarel教学编程机器人使用介绍 为了避免被编程语言固有的复杂性所困扰&#xff0c;有一个被称为卡雷尔&#xff08;Karel&#xff09;机器人的微型世界&#xff08;microworld&#xff09;的简化环境&#xff0c;可以让编程初学者从中学习理解编程的基本概念&#xff0c;而不…

【JAVA-Day44】Java常用类StringBuffer解析

Java常用类StringBuffer解析 Java常用类StringBuffer解析一、什么是StringBuffer类二、StringBuffer类的方法2.1 append方法2.2 insert方法2.3 delete方法2.4 replace方法2.5 reverse方法2.6 toString方法2.7 capacity方法2.8 length方法 三、StringBuffer类的应用场景深入了解…

SGX 研究进展

目录 SGX 应用支持技术研究进展&#xff08;笔记&#xff09;SGX 整体架构SGX安全机制隔离执行认证密封 SGX管理机制SGX 内存管理机制EPC 页驱逐(eviction)SGX SDK SGX 应用现状及瓶颈问题SGX 应用安全防护技术TCB最小化TCB 安全风险分析最小化 TCB 的安全方案 对外接口最少化E…

【Axure高保真原型】3D柱状图_中继器版

今天和大家分享3D柱状图_中继器版的原型模板&#xff0c;图表在中继器表格里填写具体的数据&#xff0c;调整坐标系后&#xff0c;就可以根据表格数据自动生成对应高度的柱状图&#xff0c;鼠标移入时&#xff0c;可以查看对应圆柱体的数据……具体效果可以打开下方原型地址体验…

3.DApp-Metamask登录不了解决方法

题记 当遇到metamask登录不了&#xff0c;加载一直转圈圈&#xff0c;可以用以下的方法解决。 切换网络 切换成Linea Mainnet测试网络&#xff1a; 输入密码 输入登录密码登录metamask 重新选择自定义网络 选择自己本地自定义的网络&#xff1a; 后记 觉得有用可以收藏或点…

【源码分析】Java中的lambda表达式会生成内部类吗?是如何生成的?

文末附结论 分析 以该程序为例子 public class LambdaTest {public static void main(String[] args) {Thread t1 new Thread(() -> {System.out.println("asdwerwerwe");});t1.start();System.out.println("end!!!");} }调用javap -c -p LambdaTest…

C/C++陷阱——临时变量的产生和特性

C/C陷阱——临时变量的产生和特性 在学习C常引用时&#xff0c;有这样一段代码引起了我的注意&#xff1a; int a 1; double& b a;当我编译这段代码时&#xff0c;竟然报错了&#xff1a; 按理来说&#xff0c;初始化引用时不能涉及权限的放大&#xff08;如用const in…

黑群晖自动调整cpu模式

DS918 黑群晖好像是不支持cpu动态频率&#xff0c;导致cpu一直在最高频率运行&#xff0c;导致功耗比较高&#xff0c;但是某些版本的系统不支持设置为自动根据cpu负载动态调整频率&#xff0c;自己写了个脚本&#xff0c;支持自动根据cpu使用率调整cpu模式 脚本下载链接如下 …

朴实无华的三天每日一题

leetcode八分钟补完三天的每日一题哈哈哈哈 主打一个求同存异 import java.util.Arrays;class Solution {public int singleNumber(int[] nums) {Arrays.sort(nums);int ans 0;for (int i 0; i < nums.length; ) {try {if (nums[i] nums[i 1]) {i 2;} else {ans nums…

讲解 CSS 过渡和动画 — transition/animation (很全面)

前言 由于用户越来越注重 Web应用 的使用体验&#xff0c;随之而来的是 Web应用 需要提供了更加完善的 Web 动画 效果来实现以平滑的状态贯穿于用户的整个使用过程中。现在&#xff0c;这已经是司空见惯了&#xff0c;用户潜意识是希望可以获得更快的反馈响应和更友好的用户界…

2023年全球新能源云母材料市场发展展望分析:储能云母市场规模快速增长[图]

云母作为电气设备的基础材料&#xff0c;下游应用领域涉及高温冶炼、电力等传统行业&#xff0c;并在近几年逐步扩展到新能源汽车、电化学储能等新兴行业。2022年&#xff0c;全球云母材料市场规模保持稳定增长至180.0亿元&#xff0c;期间年复合增长率约为13.2%。预计未来&…

React TreeSelect设置默认展开项的方法

需要实现TreeSelect组件的onTreeExpand、treeExpandedKeys方法。 代码样例如下&#xff1a; 1.TreeSelect标签部分 render() {const {codeselect} this.props;const {treeExpandedKeys} this.state ................<TreeSelectshowSearch{false}dropdownStyle{{ maxHei…

day06_面向对象基础

今日内容 1 复习 2 面向对象编程介绍 3 面向对象 类,属性,方法 ,创建对象,使用对象内存关系 一、复习 周一: jdk,配置环境变量,idea,HelloWorld 变量,基本数据类型 周二: 运算符,if,if-else 周三: if-elseif-elseif-else,循环(while,dowhile,for) 周四: 方法设计(参数,返回值),…

vue3学习(六)--- 插槽slot

文章目录 匿名插槽具名插槽作用域插槽渲染作用域 动态插槽 插槽就是&#xff1a;子组件中的提供给父组件使用的一个占位符&#xff0c;用<slot></slot> 表示&#xff0c;父组件可以在这个占位符中填充任何模板代码&#xff0c;如 HTML、组件等&#xff0c;填充的内…

C++项目实战——基于多设计模式下的同步异步日志系统-⑫-日志宏全局接口设计(代理模式)

文章目录 专栏导读日志宏&全局接口设计全局接口测试项目目录结构整理示例代码拓展示例代码 专栏导读 &#x1f338;作者简介&#xff1a;花想云 &#xff0c;在读本科生一枚&#xff0c;C/C领域新星创作者&#xff0c;新星计划导师&#xff0c;阿里云专家博主&#xff0c;C…

安全典型配置(三)使用ACL禁止特定用户上网案例

【微|信|公|众|号&#xff1a;厦门微思网络】 安全典型配置&#xff08;一&#xff09;使用ACL限制FTP访问权限案例_厦门微思网络的博客-CSDN博客本例中配置的本地用户登录密码方式为irreversible-cipher&#xff0c;表示对用户密码采用不可逆算法进行加密&#xff0c;非法用…

VulnHub lazysysadmin

一、信息收集 1.nmap扫描开发端口 开放了&#xff1a;22、80、445 访问80端口&#xff0c;没有发现什么有价值的信息 2.扫描共享文件 enum4linux--扫描共享文件 使用&#xff1a; enum4linux 192.168.103.182windows访问共享文件 \\192.168.103.182\文件夹名称信息收集&…

YOLO目标检测——抽烟吸烟数据集【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;公共场所监管、健康风险评估、戒烟干预数据集说明&#xff1a;YOLO目标检测数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富。使用lableimg标注软件标注&#xff0c;标注框质量高&#xff0c;含voc(xml)、coco(json)和yolo(txt)三…

mask-R-CNN

前言 代码 论文 # Mask-rcnn 算法在 torch vision 中有直接实现&#xff0c;可以直接引用使用在自己的工作中。 import torchvision model torchvision.models.detection.maskrcnn_resnet50_fpn(weightsMaskRCNN_ResNet50_FPN_Weights.DEFAULT)Mask R-CNN&#xff08;Mask R…