当面试官问出“Unsafe”类时,我就知道这场面试废了,祖坟都能给你问出来!

一、写在开头

依稀记得多年以前的一场面试中,面试官从Java并发编程问到了锁,从锁问到了原子性,从原子性问到了Atomic类库(对着JUC包进行了刨根问底),从Atomic问到了CAS算法,紧接着又有追问到了底层的Unsafe类,当问到Unsafe类时,我就知道这场面试废了,这似乎把祖坟都能给问冒烟啊。

但时过境迁,现在再回想其那场面试,不再觉得面试官的追毛求疵,反而为那时候青涩菜鸡的自己感到羞愧,为什么这样说呢,实事求是的说Unsafe类虽然是比较底层,并且我们日常开发不可能用到的类,但是!翻开JUC包中的很多工具类,只要底层用到了CAS思想来提升并发性能的,几乎都脱离不了Unsafe类的运用,可惜那时候光知道被八股文了,没有做到细心总结与发现。

二、Unsafe的基本介绍

我们知道C语言可以通过指针去操作内存空间,Java不存在指针,为了提升Java运行效率、增强Java语言底层资源操作能力,便诞生了Unsafe类,Unsafe是位于sun.misc包下。正如它的名字一样,这种操作底层的方式是不安全的,在程序中过度和不合理的使用,会带来未知的风险,因此,Unsafe虽然,但要慎用哦!

2.1 如何创建一个unsafe实例

我们无法直接通过new的方式创建一个unsafe的实例,为什么呢?我们看它的这段源码便知:

public final class Unsafe {// 单例对象private static final Unsafe theUnsafe;private Unsafe() {}@CallerSensitivepublic static Unsafe getUnsafe() {Class var0 = Reflection.getCallerClass();// 仅在启动类加载器`BootstrapClassLoader`加载时才合法if(!VM.isSystemDomainLoader(var0.getClassLoader())) {    throw new SecurityException("Unsafe");} else {return theUnsafe;}}
}

从源码中我们发现Unsafe类被final修饰,所以无法被继承,同时它的无参构造方法被private修饰,也无法通过new去直接实例化,不过在Unsafe 类提供了一个静态方法getUnsafe,看上去貌似可以用它来获取 Unsafe 实例。但是!当我们直接去调用这个方法的时候,会报如下错误:

Exception in thread "main" java.lang.SecurityException: Unsafeat sun.misc.Unsafe.getUnsafe(Unsafe.java:90)at com.cn.test.GetUnsafeTest.main(GetUnsafeTest.java:12)

这是因为在getUnsafe方法中,会对调用者的classLoader进行检查,判断当前类是否由Bootstrap classLoader加载,如果不是的话就会抛出一个SecurityException异常。

那我们如果想使用Unsafe类,到底怎样才能获取它的实例呢?

在这里提供给大家两种方式:

方式一

假若在A类中调用Unsafe实例,则可通过Java命令行命令-Xbootclasspath/a把调用Unsafe相关方法的类A所在jar包路径追加到默认的bootstrap路径中,使得A被启动类加载器加载,从而通过Unsafe.getUnsafe方法安全的获取Unsafe实例。

java -Xbootclasspath/a: ${path}   // 其中path为调用Unsafe相关方法的类所在jar包路径 

方式二

利用反射获得 Unsafe 类中已经实例化完成的单例对象:

public static Unsafe getUnsafe() throws IllegalAccessException {Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");//Field unsafeField = Unsafe.class.getDeclaredFields()[0]; //也可以这样,作用相同unsafeField.setAccessible(true);Unsafe unsafe =(Unsafe) unsafeField.get(null);return unsafe;}

2.2 Unsafe的使用

上面我们已经知道了如何获取一个unsafe实例了,那现在就开始写一个小demo来感受一下它的使用吧。

public class TestService {//通过单例获取实例public static Unsafe getUnsafe() throws IllegalAccessException, NoSuchFieldException {Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");//Field unsafeField = Unsafe.class.getDeclaredFields()[0]; //也可以这样,作用相同unsafeField.setAccessible(true);Unsafe unsafe =(Unsafe) unsafeField.get(null);return unsafe;}//调用实例方法去赋值public void fieldTest(Unsafe unsafe) throws NoSuchFieldException {Persion persion = new Persion();persion.setAge(10);System.out.println("ofigin_age:" + persion.getAge());long fieldOffset = unsafe.objectFieldOffset(Persion.class.getDeclaredField("age"));System.out.println("offset:"+fieldOffset);unsafe.putInt(persion,fieldOffset,20);System.out.println("new_age:"+unsafe.getInt(persion,fieldOffset));}public static void main(String[] args) {TestService testService = new TestService();try {testService.fieldTest(getUnsafe());} catch (NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}}
}
class Persion{private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}

输出:

ofigin_age:10
offset:12
new_age:20

通过 Unsafe 类的objectFieldOffset方法获取到了对象中字段的偏移地址,这个偏移地址不是内存中的绝对地址而是一个相对地址,之后再通过这个偏移地址对int类型字段的属性值进行读写操作,通过结果也可以看到 Unsafe 的方法和类中的get方法获取到的值是相同的。

三、Unsafe类的8种应用

基于Unsafe所提供的API,我们大致可以将Unsafe根据应用场景分为如下的八类,上一个脑图。
在这里插入图片描述

3.1 内存操作

学习过C或者C++的同学对于内存操作应该很熟悉了,在Java里我们是无法直接对内存进行操作的,我们创建的对象几乎都在堆内内存中存放,它的内存分配与管理都是JVM去实现,同时,在Java中还存在一个JVM管控之外的内存区域叫做“堆外内存”,Java中对堆外内存的操作,依赖于Unsafe提供的操作堆外内存的native方法啦。

内存操作的常用方法:

/*包含堆外内存的分配、拷贝、释放、给定地址值操作*/
//分配内存, 相当于C++的malloc函数
public native long allocateMemory(long bytes);
//扩充内存
public native long reallocateMemory(long address, long bytes);
//释放内存
public native void freeMemory(long address);
//在给定的内存块中设置值
public native void setMemory(Object o, long offset, long bytes, byte value);
//内存拷贝
public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes);
//获取给定地址值,忽略修饰限定符的访问限制。与此类似操作还有: getInt,getDouble,getLong,getChar等
public native Object getObject(Object o, long offset);
//为给定地址设置值,忽略修饰限定符的访问限制,与此类似操作还有: putInt,putDouble,putLong,putChar等
public native void putObject(Object o, long offset, Object x);
//获取给定地址的byte类型的值(当且仅当该内存地址为allocateMemory分配时,此方法结果为确定的)
public native byte getByte(long address);
//为给定地址设置byte类型的值(当且仅当该内存地址为allocateMemory分配时,此方法结果才是确定的)
public native void putByte(long address, byte x);

在这里我们不仅会想,为啥全是native方法呢?

  1. native方法通过JNI调用了其他语言,如果C++等提供的现车功能,可以让Java拿来即用;
  2. 需要用到 Java 中不具备的依赖于操作系统的特性,Java 在实现跨平台的同时要实现对底层的控制,需要借助其他语言发挥作用;
  3. 程序对时间敏感或对性能要求非常高时,有必要使用更加底层的语言,例如 C/C++甚至是汇编。

【经典应用】
在Netty、MINA等NIO框架中我们常常会应到缓冲池,而实现缓冲池的一个重要类就是DirectByteBuffer,它主要的作用对于堆外内存的创建、使用、销毁等工作。

通常在I/O通信过程中,会存在堆内内存到堆外内存的数据拷贝操作,对于需要频繁进行内存间数据拷贝且生命周期较短的暂存数据,都建议存储到堆外内存

在这里插入图片描述
在这里插入图片描述
从上图我们可以看到,在构建实例时,DirectByteBuffer内部通过Unsafe.allocateMemory分配内存、Unsafe.setMemory进行内存初始化,而后构建Cleaner对象用于跟踪DirectByteBuffer对象的垃圾回收,以实现当DirectByteBuffer被垃圾回收时,分配的堆外内存一起被释放。

3.2 内存屏障

为了充分利用缓存,提高程序的执行速度,编译器在底层执行的时候,会进行指令重排序的优化操作,但这种优化,在有些时候会带来 有序性 的问题。(在将volatile关键字的时候提到过了)

为了解决这一问题,Java中引入了内存屏障(Memory Barrier 又称内存栅栏,是一个 CPU 指令),通过组织屏障两边的指令重排序从而避免编译器和硬件的不正确优化情况。

在Unsafe类中提供了3个native方法来实现内存屏障:

//内存屏障,禁止load操作重排序。屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前
public native void loadFence();
//内存屏障,禁止store操作重排序。屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前
public native void storeFence();
//内存屏障,禁止load、store操作重排序
public native void fullFence();

【经典应用】
在之前的文章中,我们讲过Java8中引入的一个高性能的读写锁:StampedLock(锁王),在这个锁中同时支持悲观读与乐观读,悲观读就和ReentrantLock一致,乐观读中就使用到了unsafe的loadFence(),一起去看一下。

	/*** 使用乐观读锁访问共享资源* 注意:乐观读锁在保证数据一致性上需要拷贝一份要操作的变量到方法栈,并且在操作数据时候					可能其他写线程已经修改了数据,* 而我们操作的是方法栈里面的数据,也就是一个快照,所以最多返回的不是最新的数据,但是一致性还是得到保障的。** @return*/double distanceFromOrigin() {long stamp = sl.tryOptimisticRead(); // 获取乐观读锁double currentX = x, currentY = y;	// 拷贝共享资源到本地方法栈中if (!sl.validate(stamp)) { // //检查乐观读锁后是否有其他写锁发生,有则返回falsestamp = sl.readLock(); // 获取一个悲观读锁try {currentX = x;currentY = y;} finally {sl.unlockRead(stamp); // 释放悲观读锁}}return Math.sqrt(currentX * currentX + currentY * currentY);}

在官网给出的乐观读的使用案例中,我们看到if中做了一个根绝印章校验写锁发生的操作,我们跟入这个校验源码中:

public boolean validate(long stamp) {U.loadFence();//load内存屏障return (stamp & SBITS) == (state & SBITS);}

这一步的目的是防止锁状态校验运算发生重排序导致锁状态校验不准确的问题!

3.3 对象操作

其实在2.2 Unsafe的使用中,我们已经使用了Unsafe进行对象成员属性的内存偏移量获取,以及字段属性值的修改功能了,除了Int类型,Unsafe还支持对所有8种基本数据类型以及Object的内存数据修改,这里就不再赘述了。

需要额外强掉的一点,在Unsafe的源码中还提供了一种非常规的方式进行对象的实例化:

//绕过构造方法、初始化代码来创建对象
public native Object allocateInstance(Class<?> cls) throws InstantiationException;

这种方法可以绕过构造方法和初始化代码块来创建对象,我们写一个小demo学习一下。

@Datapublic class A {private int b;public A(){this.b =1;}}

定义一个类A,我们分别采用无参构造器、newInstance()、Unsafe方法进行实例化。

public void objTest() throws Exception{A a1=new A();System.out.println(a1.getB());A a2 = A.class.newInstance();System.out.println(a2.getB());A a3= (A) unsafe.allocateInstance(A.class);System.out.println(a3.getB());}

输出结果为1,1,0。这说明调用unsafe的allocateInstance方法确实可以跳过构造器去实例化对象!

3.4 数组操作

在 Unsafe 中,可以使用arrayBaseOffset方法获取数组中第一个元素的偏移地址,使用arrayIndexScale方法可以获取数组中元素间的偏移地址增量,通过这两个方法可以定位数组中的每个元素在内存中的位置。

基于2.2 Unsafe使用的测试代码,我们增加如下的方法:

  //获取数组元素在内存中的偏移地址,以及偏移量private void arrayTest(Unsafe unsafe) {String[] array=new String[]{"aaa","bb","cc"};int baseOffset = unsafe.arrayBaseOffset(String[].class);System.out.println("数组第一个元素的偏移地址:" + baseOffset);int scale = unsafe.arrayIndexScale(String[].class);System.out.println("元素偏移量" + scale);for (int i = 0; i < array.length; i++) {int offset=baseOffset+scale*i;System.out.println(offset+" : "+unsafe.getObject(array,offset));}}

输出:

数组第一个元素的偏移地址:16
元素偏移量4
16 : aaa
20 : bb
24 : cc

3.5 CAS相关

终于,重点来了,我们写这篇文章的初衷是什么?是回想起曾经面时,面试官由原子类库(Atomic)问到了CAS算法,从而追问到了Unsafe类上,在JUC包中到处都可以看到CAS的身影,在java.util.concurrent.atomic相关类、Java AQS、CurrentHashMap等等类中均有!

以AtomicInteger为例,在内部提供了一个方法为compareAndSet(int expect, int update) ,如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update),而它的底层调用则是unsafe的compareAndSwapInt()方法。

public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}

CAS思想的底层实现其实就是Unsafe类中的几个native本地方法:

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

3.6 线程调度

Unsafe 类中提供了park、unpark、monitorEnter、monitorExit、tryMonitorEnter方法进行线程调度,在前面介绍 AQS 的文章中我们学过,在AQS中通过调用LockSupport.park()和LockSupport.unpark()实现线程的阻塞和唤醒的,而LockSupport的park、unpark方法实际是调用Unsafe的park、unpark方式来实现。

//取消阻塞线程
public native void unpark(Object thread);
//阻塞线程
public native void park(boolean isAbsolute, long time);
//获得对象锁(可重入锁)
@Deprecated
public native void monitorEnter(Object o);
//释放对象锁
@Deprecated
public native void monitorExit(Object o);
//尝试获取对象锁
@Deprecated
public native boolean tryMonitorEnter(Object o);

LockSupport源码:

public static void park(Object blocker) {Thread t = Thread.currentThread();setBlocker(t, blocker);UNSAFE.park(false, 0L);setBlocker(t, null);}public static void unpark(Thread thread) {if (thread != null)UNSAFE.unpark(thread);}

3.7 Class操作

Unsafe 对Class的相关操作主要包括静态字段内存定位、定义类、定义匿名类、检验&确保初始化等。

//获取给定静态字段的内存地址偏移量,这个值对于给定的字段是唯一且固定不变的
public native long staticFieldOffset(Field f);
//获取一个静态类中给定字段的对象指针
public native Object staticFieldBase(Field f);
//判断是否需要初始化一个类,通常在获取一个类的静态属性的时候(因为一个类如果没初始化,它的静态属性也不会初始化)使用。 当且仅当ensureClassInitialized方法不生效时返回false。
public native boolean shouldBeInitialized(Class<?> c);
//检测给定的类是否已经初始化。通常在获取一个类的静态属性的时候(因为一个类如果没初始化,它的静态属性也不会初始化)使用。
public native void ensureClassInitialized(Class<?> c);
//定义一个类,此方法会跳过JVM的所有安全检查,默认情况下,ClassLoader(类加载器)和ProtectionDomain(保护域)实例来源于调用者
public native Class<?> defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain);
//定义一个匿名类
public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches);

【测试案例】

@Datapublic class User {public static String name="javabuild";int age;}private void staticTest() throws Exception {User user=new User();//判断是否需要初始化一个类,通常在获取一个类的静态属性的时候(因为一个类如果没初始化,它的静态属性也不会初始化)使用System.out.println(unsafe.shouldBeInitialized(User.class));Field sexField = User.class.getDeclaredField("name");//获取给定静态字段的内存地址偏移量long fieldOffset = unsafe.staticFieldOffset(sexField);//获取一个静态类中给定字段的对象指针Object fieldBase = unsafe.staticFieldBase(sexField);//根据某个字段对象指针和偏移量可以唯一定位这个字段。Object object = unsafe.getObject(fieldBase, fieldOffset);System.out.println(object);}

此外,在Java8中引入的Lambda表达式的实现中也使用到了defineClass和defineAnonymousClass方法。

3.8 系统信息

Unsafe 中提供的addressSize和pageSize方法用于获取系统信息。

1) 调用addressSize方法会返回系统指针的大小,如果在 64 位系统下默认会返回 8,而 32 位系统则会返回 4。

2) 调用 pageSize 方法会返回内存页的大小,值为 2 的整数幂。

使用下面的代码可以直接进行打印:

private void systemTest() {System.out.println(unsafe.addressSize());System.out.println(unsafe.pageSize());
}

输出为:8,4096

四、总结

哎呀,妈呀,终于写完了,人要傻了,为了整理这篇文章看了大量的源码,人看的头大,跟俄罗斯套娃似的源码,严谨的串联在一起!Unsafe类在日常的面试中确实不经常被问到,大家稍微了解一下即可。

五、结尾彩蛋

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!
在这里插入图片描述
如果您想与Build哥的关系更近一步,还可以关注“JavaBuild888”,在这里除了看到《Java成长计划》系列博文,还有提升工作效率的小笔记、读书心得、大厂面经、人生感悟等等,欢迎您的加入!
在这里插入图片描述

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

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

相关文章

前后端开发入门全攻略:零基础学起

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、前后端开发概览 二、后端开发基础&#xff1a;Flask框架入门 代码案例&#xff1a;Hel…

vue3之使用图片实现类似于 el-radio 的单选框功能,并且可实现选中和取消选中

背景 我们在工作中常用的一般都是使用类似于 element-plus 中的 el-radio 或者是 el-checkbox 来实现单选或者多选 若有一天我们遇到了一个新的业务需求,需要使用 图片 来实现类似于 el-radio 的功能,并且要求实现第一次点击时处于选中状态,当我们再次点击时处于非选中状态…

谈恋爱没经验?那就来刷谈恋爱经验宝宝吧

❤️作者主页&#xff1a;小虚竹 ❤️作者简介&#xff1a;大家好,我是小虚竹。2022年度博客之星评选TOP 10&#x1f3c6;&#xff0c;Java领域优质创作者&#x1f3c6;&#xff0c;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;掘金年度人气作…

自动驾驶---Tesla的自动驾驶技术进化史(PerceptionPlanning)

1 前言 笔者在专栏《自动驾驶Planning模块》中已经详细讲解了传统自动驾驶Planning模块的内容&#xff1a;包括行车的Behavior Planning和Motion Planning&#xff0c;以及低速记忆泊车的Planning&#xff08;最开始有15篇&#xff0c;目前逐渐更新到17篇&#xff09;。读者对整…

【Spring】SSM介绍_SSM整合

1、SSM介绍 1.1简介 SSM&#xff08;Spring SpringMVC MyBatis&#xff09;整合是一种流行的Java Web应用程序框架组合&#xff0c;它将Spring框架的核心特性、SpringMVC作为Web层框架和MyBatis作为数据访问层框架结合在一起。这种整合方式提供了从数据访问到业务逻辑处理再…

5.18 TCP机械臂模拟

#include <netinet/tcp.h>//包含TCP选项的头文件 #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <linux/input.h>//读取输入事件 #include <sys/types.h> #include <sys/stat.h&…

一文详解逻辑越权漏洞

1. 逻辑越权 1.1. 漏洞原理 逻辑越权漏洞就是当用户跳过自己的权限限制&#xff0c;去操作同等级用户或者上级用户。正常的情况下&#xff0c;当一个用户去访问某个资源的时候&#xff0c;首先需要去登录验证自己的权限&#xff0c;其次是对数据的查询&#xff0c;最后返回数…

linux命令中arpd的使用

arpd 收集免费ARP信息 补充说明 arpd命令 是用来收集免费arp信息的一个守护进程&#xff0c;它将收集到的信息保存在磁盘上或者在需要时&#xff0c;提供给内核用户用于避免多余广播。 语法 arpd(选项)(参数)选项 -l&#xff1a;将arp数据库输出到标准输出设备显示并退出…

【云原生】Kubernetes----POD基本管理

目录 引言 一、Pod基础概念 &#xff08;一&#xff09;Pod简介 &#xff08;二&#xff09;Pod的分类 1.自主式Pod 2.控制器管理的Pod &#xff08;三&#xff09;Pod使用方式 1.单容器pod 2.多容器Pod 3. 注意事项 二、Pod容器的分类 &#xff08;一&#xff09;…

【Unity】免费的高亮插件——QuickOutline

除了常见的HighLightSystem来实现的高亮功能&#xff0c;其实还有很多的方法实现物体的高亮。 在 Unity资源商店 搜索OutLine&#xff0c;就会有很多免费好用的高亮插件。 下面介绍一下 QuickOutline这个插件&#xff0c;在 Unity资源商店 搜索到后&#xff0c;点击进去就可以…

推荐几款新手学习编程的网站

免费在线开发平台 介绍一款编程平台&#xff0c;专为学生和开发者量身打造&#xff01;平台拥有近4000道编程题目&#xff0c;支持多种编程语言&#xff08;包括C、C、JavaScript、TypeScript、Go、Rust、PHP、Java、Ruby、Python3和C#&#xff09;&#xff0c;为您提供全面的学…

Tomcat端口配置

Tomcat是开源免费的服务器&#xff0c;其默认的端口为8080&#xff0c;本文讲述一下如何配置端口。 最后在浏览器中输入localhost:8888即可打开Tomcat界面

python判断字符串是否为回文串的详细解析与实现

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言&#xff1a;回文串的定义与背景 二、判断回文串的基本思路 示例解析 三、代码实…

三维场景感知之三维目标检测方向入门

三维目标检测入门 1 文档需知2 基础知识深度学习基础必上手项目科研研究必知道的论文门户深度学习必看论文 3 目标检测入门知识二维目标检测必看论文 4 三维目标检测入门知识三维目标检测必熟悉数据集三维目标检测点云分类分割预备知识三维目标检测必熟悉&#xff0c;必跑通&am…

Node.js —— 前后端的身份认证 之用 express 实现 JWT 身份认证

JWT的认识 什么是 JWT JWT&#xff08;英文全称&#xff1a;JSON Web Token&#xff09;是目前最流行的跨域认证解决方案。 JWT 的工作原理 总结&#xff1a;用户的信息通过 Token 字符串的形式&#xff0c;保存在客户端浏览器中。服务器通过还原 Token 字符串的形式来认证用…

AIGC-风格迁移-“DEADiff:稳定可控的文本到图像风格化扩散模型 “-CVPR2024

DEADiff: An Efficient Stylization Diffusion Model with Disentangled Representations 代码&#xff1a;https://tianhao-qi.github.io/DEADiff/ 论文&#xff1a;https://arxiv.org/pdf/2403.06951 本文介绍了一种名为DEADiff的方法&#xff0c;旨在解决基于扩散的文本到图…

【机器学习论文阅读笔记】Robust Recovery of Subspace Structures by Low-Rank Representation

前言 终于要轮到自己汇报了好崩溃。。盯着论文准备开始做汇报ppt感觉一头乱麻&#xff0c;决定还是写博客理清思路再说吧 参考资料&#xff1a; 论文原文&#xff1a;arxiv.org/pdf/1010.2955 RPCA参考文章&#xff1a;RPCA - 知乎 (zhihu.com) 谱聚类参考文章&#xff1a…

Python使用pymysql操作数据库

大家好&#xff0c;当涉及到与数据库进行交互和操作时&#xff0c;Python的pymysql库是一个常用且功能强大的选择。pymysql提供了与MySQL数据库的连接、查询、插入、更新和删除等操作的方法&#xff0c;使得在Python中进行数据库操作变得简单而高效。 1、安装 pymysql 库 在开…

Python3 笔记:部分专有名词解释

1、python 英 /ˈpaɪθən/ 这个词在英文中的意思是蟒蛇。但据说Python的创始人Guido van Rossum&#xff08;吉多范罗苏姆&#xff09;选择Python这个名字的原因与蟒蛇毫无关系&#xff0c;只是因为他是“蒙提派森飞行马戏团&#xff08;Monty Python&#xff07;s Flying Ci…

【静态分析】在springboot使用太阿(Tai-e)01

参考&#xff1a;使用太阿&#xff08;Tai-e&#xff09;进行静态代码安全分析&#xff08;spring-boot篇一&#xff09; - 先知社区 ---------------------------------------------------------------------- 由于spring-boot实现了控制反转与面向切面编程的设计思想&#x…