多线程进阶
一.常见的锁策略
这里所讲的锁,不是一把具体的锁,而是锁的特性
1.乐观锁和悲观锁
悲观乐观是对锁冲突大小的预测
若预测锁冲突概率不大,就可能会少一些工作,那就是乐观锁;反之就是悲观锁
总是假设最坏的情况,每次读取数据时都认为别人要改数据,所以在每次读取数据时都要上锁。这就是悲观锁
总是假设一般情况下不会产生冲突,所以在对数据进行提交更新时才会正式对数据是否产生并发冲突进行检查,如果发现冲突了,就返回错误的信息,让用户决定接下来怎么做
2.重量级锁和轻量级锁
锁的开销大就是重量级锁,对应悲观锁;锁的开销小就是轻量级锁,对应乐观锁
3.自旋锁和挂起等待锁
自旋锁是一种轻量级锁的典型实现;挂起等待锁是一种重量级锁的典型实现
自旋锁往往是一种纯用户态的实现。在之前,如果一个线程没有拿到锁,就会进入阻塞等待,也就是放弃cpu,需要过很久才能再次被调度。但是要是用了自旋锁,就不是这样的了。自旋锁相当于有一个while循环,如果获取锁失败,就会立即进行下一次获取锁的尝试,无限循环,直到第一次获得到锁。(这就是我们所说的忙等,虽然很消耗cpu,但是换来的却是更快的响应速度,因为他没有放弃cpu,当得到锁时,不用再次等待cpu调度的过程,而是直接就可以进行程序)
挂起等待锁:当线程没有申请到锁时,就会被挂起等待,即加入到等待队列阻塞等待;当锁被释放时,再重新竞争。(所以自旋锁也可以说为:当线程没有获得锁时,不会被观其等待,而是隔一段时间再去检查一下锁是否被释放)
4.读写锁
这里的读写锁和数据库的读写锁不同(数据库中,写加锁就是再写时不可以读,读加锁就是在读时不可以写。)
在这里,读加锁就是在读时能读但不能写,而写加锁就是不能读也不能写。
读写锁把枷锁操作分成了读锁和写锁。
在俩线程加锁过程中:读锁和读锁之间不会产生竞争(因为多线程下的读操作无线程安全问题);而读锁和写锁之间有竞争;写锁和写锁之间也有竞争。
5.可重入锁和不可重入锁
一个线程针对同一把锁先后枷锁俩次,没有出现死锁现象,就是可重入锁
6.公平锁和非公平锁
当很多线程尝试加同一把锁时,只有一个线程能够拿到锁,剩下的线程就得阻塞等待。一旦第一个线程释放了锁,接下来那个线程可以拿到锁?这时有俩种机制:
对于公平锁:会根据先来后到的顺序进行锁的分配
对于非公平锁:剩下的线程会以均等的机会来竞争锁。
操作系统提供的api一般是非公平锁,要想实现公平锁,就得引入额外的队列,来维护这些线程的加锁顺序。
那么synchronized属于哪些呢?
1.对于悲观乐观:是自适应的
2.重量轻量:自适应的
3.自旋锁/挂起等待锁:自适应的
初始时,synchronized会预测当前的锁冲突概率不大,此时以乐观锁的方式运行(也就是轻量级锁,基于自旋锁的方式实现);后来使用过程中,发现锁冲突较多,就升级成悲观锁(也就是重量级锁,基于挂起等待锁的方式实现)。所以说synchronized会自动记录冲突情况
4.它不是读写锁
5.是可重入锁
6.是非公平锁
二.CAS
CAS就是Compare And Swap,即比较并交换。它是一个cpu指令,一个指令,表示这是原子的
1.CAS基本思路
什么是CAS,我们来解释一下:
假设M是内存,A,B是寄存器,CAS(M,A,B):如果M和A的值相同,就把M和B的值交换,并返回true;如果M和A的值不同,就无事发生,返回false。交换的本质其实是赋值,就是把B的值赋给M(寄存器B的值是啥,我们不关心,我们只关心M中的内容)
2.CAS优点:保证线程安全,避免阻塞影响效率
cas是一个cpu指令,也就是上述逻辑的完成不是分步进行的,而是原子的。
所以可以使用CAS哇变成一些操作,进一步替代加锁,给编写线程安全的代码,引入了新思路。
基于CAS实现线程安全,就是无锁编程!!!省去了加锁时的消耗
CAS本质上是cpu提供的指令,又被操作系统进行封装,提供成api,然后又被JVM进行封装,提供成新的api以供程序员使用(代码)
3.CAS缺点
1.代码更复杂,不好理解
2.只适用于一些特定场景,不如加锁具有普适性
4.CAS的应用
1.实现原子类
在Java标准库的java.util.concurrent.atomic包中,提供了许多原子类,就是基于CAS实现的
AtomicInteger
根据之前的学习,我们知道int数据进行加加操作不是原子的,而是有三步,这就会有线程安全问题,而AtomicInteger,基于CAS方式,对int进行了封装,此时的加加操作就是原子的了。使用如下:
创建对象时,先传入一个初始值,getAndIncrement相当于后置加加,incrementAndGet相当于前置加加
注意这个方法,调用了unsafe的方法!!!!为什么呢?
在Java中,有些操作是偏底层的,偏底层操作会有很多注意事项,稍有不慎就会写出问题,这些操作就会归为unsafe类
看unsafe里面的这个方法,里面有调用了compareAndSwapInt,这其实就是那个CAS操作,但是再点进去,就看不到具体实现了:
它是native、修饰的,就是本地方法,也就是在JVM源码中使用c++代码实现的
伪代码实现:
我们来用上面的伪代码来学习一下CAS的思路,实现原理:
在while循环中:如果this.val=oldval(this.val是刚刚从内存上读的,而oldval是之前读到的,已经存放在寄存器A中了),那么说明没有其他线程对val进行修改,那么就把oldval+1的值赋值给this.val,也就是实现了一次加加操作,同时返回true,由于前面有一个!,所以会出了while,结束加加操作。
但要是this.val!=oldval,就说明有其他线程对内存中的val值进行了修改,所以就返回了false,加上前面的!,就会进入while语句块,对oldval的值进行更新,但因为这次的++操作没有进行下去,所以会在进行CAS指令。
具体我们来举一个俩个线程的例子,首先把getAndIncrement方法分成俩步:
这是第一步
这是第二步
然后后面是俩个线程的执行顺序:
t1 t2
第一步
第一步
第二步
第二步
在t1执行1和2之间,t2已经进行一一次完整的加加操作。假设t1第一步读到的oldval值为1,然后t2进行加加,那么内存里的val值就是2了,那么这是时1进行第二步时,this.val和oldval的值就不相同,就不会进行赋值操作,并且返回false,然后就可以进入到while内部,更新oldval的值为2
总结:
CAS是让这里的自增不要穿插进行,它是通过重试的方式避免穿插的,一旦发现oldval的值与内存中的值不相同,就说明有人穿插进来了。
而要是加锁,就是通过阻塞来避免穿插执行。
2.实现自旋锁(需要靠代码来进行纯用户态的实现)
伪代码实现:
owner是记录当前的自旋锁被哪个线程获取
如果owner==null(说明当前的锁是空闲的,没有现成使用该把锁)那么就把当前的线程赋值给owner。如果这个锁已经被别的线程持有即owner!=null,那么就自旋等待
这个锁的劣势就是一直消耗cpu资源,忙等,所以该锁要看场景使用
5.CAS的ABA问题
由上面的讲述可知,CAS是根据值不变来作为“没有其他线程穿插执行的”判定依据,但这不够严谨,比如可能遇到ABA问题,就是其他线程将数据从A改成了B,又从B改回了A。
一般情况下,ABA问题不会出现bug,因为在逻辑上没啥问题,但是也会有极端情况:
比如ATM取钱:账户中有1000元,我要取500元,比如出现了bug,我按了一下没有反应,所以就又按了一下,这时候就有俩个线程:
t1 t2
int oldval=val(1000);
int oldval=val(1000);
CAS(this.val,oldval,oldval-500);
CAS(this.val,oldval,oldval-500);
t1线程在CAS之前,t2线程已经进行了CAS,对于t2来说,this.val==oldval==1000,所以t2已经完成了扣款操作。到了t1进行CAS时,由于this.val(500)!=oldval(1000),所以就不进行扣款了。这里的CAS原子操作保证了在出现bug按了俩次时没有多扣款
但是如果现在在t2的CAS和t1的CAS操作之间又来了一个t3线程,比如我妈给我转账500元,那么这时this.val就又是1000元了,然后t1再CAS时就会成功扣款了,所以就一共扣了俩次款,但我只取到了500元。
如何解决ABA问题
只要让判定的数值按一个方向增长即可(有增有减就有可能出现ABA问题,但只是增或减就没问题)
但针对像账户余额这样的概念,本身就得有增邮件,那么我们就可以引入一个版本号,每次修改都让他进行自增,此时CAS进行判定时,就不仅仅直接判定余额,而是还要判定版本号。
三.synchronized几个重要的机制
1.锁升级
在代码执行过程中,synchronized会根据当前情况适当进行锁升级过程,升级顺序为:无锁->偏向锁->自旋锁->重量级锁
什么是偏向锁?
类比于线程池,线程池是优化了找“下一任”的效率,而偏向锁则是优化了“分手”的效率。比如我看上了一个小哥哥,立马就和他确立了情侣关系,但到了后期我不耐烦时,想要和他分手,就得考虑很多,还得找理由,这样的分手就低效;但要是我看上了一个小哥哥,但从不提交往,只是培养感情,感情越来越好,但没有确认关系,当我对他不耐烦时,只要不理会他就行,因为我俩也没啥关系,是自由的。这就是偏向锁的状态。
偏向锁不是真正的加锁,只是做了一个标记。
如果在我和小哥哥培养感情的时候又来了一个妹子接近小哥哥,针对这种情况,我就立马想小哥哥进行表白确立关系。就是一旦出现锁竞争威胁,一旦出现锁竞争的可能,偏向锁就可以立即升级为真正加锁
所以偏向锁的核心思想就是“懒汉模式”,能不加锁就不加锁,因为只要加锁就会有开销。
总结:
锁升级过程就是在性能和线程安全之间尽量进行权衡
2.锁粗化
首先了解一下锁的粒度:synchronized代码块中,代码越多,锁的粒度越粗。
而锁的粒度细时:能够并发执行的逻辑就越多,更利于充分利用多核cpu资源,但太细的锁被反复加锁解锁,实际效果或者效率可能还不如粒度粗的锁
此时编译器的优化机制:一段逻辑中要是多次出现加锁解锁,编译器+JVM就会自动进行锁的粗化
3.锁消除
这也是编译器的优化机制:编译器和JVM会针对当前写到加锁代码进行盘底你个,若觉得不需要加锁,就会把synchronized优化掉。
例如:StringBuilder不带synchronized,StringBuffer带有synchronized。但若是在单个线程中使用buffer,编译器就会自动把锁优化掉(但编译器只在自己非常有把握的情况下进行锁消除,所以触发的概率并不高)
四.JUC(java.util.concurrent)常见的类
1.Callable接口
Callable接口也是一个创建线程的方式。适用于想让某个线程执行一个逻辑并返回一个结果的。
这就是和Runnable的区别,runnable不关注结果,而Callable关注结果
例如:创建一个线程,返回1到100的加和结果
不用Callable接口时:
如果没有Callable接口,代码就得如下这样写:
最终的计算结果得用一个自定义的类来封装。但我这样写对吗?
执行后,发现结果是0!!这是为什么?因为t线程还没有执行完,而主线程就已经执行完了!!。所以我们应该怎么写呢?
可以如上加一个t.join()等待线程执行完毕。但还有一个问题,如果又有另一个线程对rusult的值进行修改怎么办?为了不被修改,我们应该对有些地方进行加锁:
使用Callable接口时:
Callable接口是一个泛型接口,他只有一个抽象方法call,我们重写他即可
然后将这个callable封装到futuretask中,然后创建线程传入futuretask,然后启动线程,让线程执行call方法完成计算,最终的计算结果就放到了futuretask当中。最后注意:拿结果时用的是futuretask,而不是t线程。同时futuretask.get方法能够触发阻塞,等待t线程把结果计算完毕
理解Callable:
Callable和Runnable是同一级别的,只不过一个可以返回结果,另一个无法返回结果
Callable需要搭配一个FutureTask来使用,它用来保存callable的结果。因为callable自己没有等待功能,所以futuretask就可以负责等待这一个工作
理解FutureTask:
想象一下我们去喝奶茶,服务员会给我们一个小票,上面写着取奶茶的号,我们要凭小票取奶茶。这里,点单就相当于是告诉了call,应该干什么;小票就相当于是futuretask,只有叫了上面的号,我们才结束等待;后厨就相当于是一个线程,开始做奶茶;最后我们就凭小票取奶茶,也就是get方法
面试题小结:
创建线程的几个方法:
1.继承Thread重写run(创建单独的类或使用匿名内部类)
2.实现Runnable重写run(创建单独的类或使用匿名内部类)
3.实现Callable重写call(创建单独的类或使用匿名内部类)
4.使用lambda表达式
5.使用线程工厂ThreadFactory
6.使用线程池
2.ReentrantLock
也是叫做可重入锁,不过,它不是一个简单的关键字,而是一个类,它比synchronized使用起来更麻烦
使用方式:
lock方法进行加锁,unlock方法进行解锁
所以一定不能忘记unlock!!!如何能不忘记unlock呢?用try finally
ReentrantLock的优势:
1.它的加锁有俩种方式:lock和trylock
对于lock来说,当加锁失败时,就会进行阻塞等待;而trylock没有加上锁就会直接放弃
阻塞等待有时候并不是一个好的选择,有时候死等就不如放弃,trylock就给了我们更多操作空间
2.reentranlock提供了公平锁的实现。默认情况下,它是非公平锁,但我们可以在他的构造方法中传入参数,表示需要公平锁
3.reentrantlock提供了更强大的等待通知机制。它搭配了Condition类,来实现等待通知(例如:可规定想要唤醒哪个线程)
虽然有以上优势,但是,加锁首选synchronized,因为reentrantlock使用起来更复杂,尤其是容易忘记解锁。
3.信号量(Semaphore)
什么是信号量:
想象一下停车场门口有一个显示牌,显示剩余几个车位,这就好比与信号量。
信号量就是一个计数器,描述了可用资源的个数,每次申请(P操作)释放(V操作)一个可用资源时,信号量就会自动加一或减一(这里的加减操作是原子的)。
例如初始有100个可用资源(即信号量初始为100)当进行了100次P操作后,再进行一次P操作就会进入阻塞等待,有一种上锁的感觉。
锁其实就是一个特殊的信号量,可用资源为1的信号量。加锁:1->0;解锁:0->1,这样的信号量也叫二元信号量。
操作系统提供了信号量的实现,并封装成了api,JVM有进一步封装了此api,就可以再java代码中使用了。
代码实例:
这时就一直等待下去
4.CountDownLatch
理解CountDownLatch:
适用于多个线程来完成一系列任务时,用来衡量任务进度是否完成。比如:将一个大任务分成多个小任务,让这些小任务都并发执行。这时就可以用countdownlatch来判定小任务是否都完成了,如果没有完成,就会进入等待。
主要方法:
await方法:调用await时会触发阻塞,只有当所有小人物都完成了,await才会返回,才能继续进行下去
countDown方法:在每个分任务执行完毕后,都要调用countDown方法,来告诉CountDownLatch我这个任务执行完了。
使用示例:
创建一个实例,然后放置10个任务,每个任务完成后都要调用countDown,结果如下:
但要是执行到任务不够10个,那就会阻塞等待
五.线程安全的集合类
1.多线程环境使用ArrayList
自己使用synchronized或ReentrantLock去调节
使用Colletions.synchronizedList(new ArrayList());
在Collections类中,这是一个类方法,直接用类名调用,返回一个新对象synchronizedList,它实现了List接口,该类相当于是标准库中提供的一个基于synchronized进行线程同步的List,也就是在List中的关键方法上加上synchronized,简单来看一下:
使用CopyOnWriteArrayList
copyonwrite即写时拷贝,例如:俩个线程同时使用ArrayList。若俩个线程同时读,就直接读;若某个线程在改,那么就把ArrayList复制一份副本,改的时候就改那个副本,而读的那个线程就继续读原List。改完后,就会使用修改好的这份数据替代元数据(往往是引用赋值,就是将原List的引用指向新的引用)
但注意:
1.ArrayList不能太大,否则copy的开销就大
2.更适合于一个线程改,其他线程读,而而不适用于多个线程改!!!
2.多线程环境下使用哈希表
HashTable:
这个类是在方法前面加上了synchronized,相当于是对this上锁,也就是对整个哈希表上锁。这就导致了只要有俩个线程操作同一个哈希表时就会引起冲突
ConCurrentHashMap:
但是我们知道,hash表的结构是一个数组,每个元素又是一个链表的表头(这是用来解决hash冲突的)可是当操作不同链表时,不会有线程安全,只有操作同一个链表时才会有线程安全问题。所以根本没必要对整个哈希表都上锁,而仅仅以链表为单位进行上锁即可。所以concurrenthashmap就对哈希表进行了如下改进:
1.最核心的改进:把一个全局的大锁改成了每个链表独立的小锁,大幅度降低了锁冲突的频率,等同于提高了效率
2.充分利用了CAS特性,把一些没必要加锁的环节省略加锁了(比如:记录hash表中元素个数时,可用原子操作对size进行加减操作)
3.激进操作:针对读操作没有加锁。这样,读与读和读与写之间都没有竞争。
那么是否会出现读到了修改了一半的数值的情况呢?ConCurrentHashMap在底层编码时,谨慎处理了一些操作,比如修改时避免使用++--这样的非原子的操作,而是用=进行修改,所以不会出现以上情况。
4.concurrenthashmap针对扩容操作进行了优化:本身hashmap/hanshset在扩容时会将表全都拷贝一遍,担当元素多时就容易卡顿,拷贝也耗时。
所以concurrenthashmap就化整为零,一旦需要扩容,确实是要搬运,这不过不是一次性搬运完,第一次先创建出足够大小的数组并把新数写上去,第二次添加元素时,就会把部分旧元素拷贝过去,第三次以后也是这样。
相关文章:

多线程进阶
一.常见的锁策略 这里所讲的锁,不是一把具体的锁,而是锁的特性 1.乐观锁和悲观锁 悲观乐观是对锁冲突大小的预测 若预测锁冲突概率不大,就可能会少一些工作,那就是乐观锁;反之就是悲观锁 总是假设最坏的情况&…...
总结linux常用命令
Linux常用命令总结如下: 文件与目录操作: ls:列出目录内容cd:改变当前目录pwd:显示当前工作目录mkdir:创建新目录cp:复制文件或目录rm:删除文件或目录mv:移动或重命名文件…...
C++ 枚举
C 枚举 5.4.1普通枚举 枚举的定义:,枚举类型是通过enum关键字定义的,比如定义颜色类型 enum Color {RED, // 默认值为0GREEN, // 默认值为1BLUE // 默认值为2 }; Color myColor RED;注意: (1)括…...

Vue2在一个页面内动态切换菜单显示对应的路由组件
项目的需求是在一个页面内动态获取导航菜单,导航菜单切换的时候显示对应的路由页面,类似于tab切换的形式,切换的导航菜单和页面左侧导航菜单是同一个路由组件,只是放到了一个页面上,显示的个数不同,所有是动…...
执行任务赚积分C卷(JavaPythonC++Node.jsC语言)
现有N个任务需要处理,同一时间只能处理一个任务,处理每个任务所需要的时间固定为1。 每个任务都有最晚处理时间限制和积分值,在最晚处理时间点之前处理完成任务才可获得对应的积分奖励。 可用于处理任务的时间有限,请问在有限的时间内,可获得的最多积分。 输入描述 第一…...

接口测试之文件下载
在工作中对于下载接口,经常会有这样的疑问:这类接口一般功能比较稳定,但是又比较重要,需要占用回归测试时间,有没有可替代的方式? 答案肯定是有的,可以从接口测试/UI自动化测试介入,…...

算法思想总结:双指针算法
一、移动零 . - 力扣(LeetCode) 移动零 该题重要信息:1、保持非0元素的相对位置。2、原地对数组进行操作 思路:双指针算法 class Solution { public:void moveZeroes(vector<int>& nums){int nnums.size();for(int cur…...
python中的zip函数
1.zip()同时迭代多个列表、字典等 使用zip()可以同时迭代多个可迭代对象,如列表、字典。 注意:当若干个可迭代对象的长度不相等时,zip()函数会停止在最短的可迭代对象。 例子: # 定义可迭代对象 numbers …...

Element 选择季度组件
<template><el-dialogtitle"选择季度":show-close"false":close-on-click-modal"false":close-on-press-escape"false":visible"visiable"class"dialog list"append-to-body><div><div>&…...

4.MongoDB中16个常用CURD
基本的CURD 作为一个非专业的DBA,我们只需要会一些基本的curd就行,专业的内容还是需要专业的人去干的。CRUD 也就是增删改查,这是数据库最基本的功能,查询还支持全文检索,GEO 地理位置查询等。 01创建库 无需单独创…...
Tomcat数据源笔记
Tomcat数据源笔记 连接池的概念 连接池是一种由容器提供的机制,用于管理数据库连接对象的集合。连接池的主要作用是在应用程序需要与数据库进行交互时,提供可复用的连接对象,从而减少每次建立数据库连接的开销。 连接池的工作原理 连接池的…...
Spring-Kafka笔记整理
引入依赖<dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId> </dependency>配置application.propertiesspring.kafka.bootstrap-servers192.168.99.51:9092编写kafka的配置类Configuration …...
已解决org.apache.hadoop.hdfs.protocol.QuotaExceededException异常的正确解决方法,亲测有效!!!
已解决org.apache.hadoop.hdfs.protocol.QuotaExceededException异常的正确解决方法,亲测有效!!! 目录 问题分析 报错原因 解决思路 解决方法 总结 博主v:XiaoMing_Java 问题分析 在使用Hadoop分布式文件系统&a…...

GitHub打不开的解决方案(超简单)
在国内,github官网经常面临打不开或访问极慢的问题,不挂VPN(梯子,飞机,魔法)使用体验极差,那有什么好办法解决github官网访问不了的问题?今天小布教你几招轻松访问github官网。 git…...

Unity开发一个FPS游戏之二
在之前的文章中,我介绍了如何开发一个FPS游戏,添加一个第一人称的主角,并设置武器。现在我将继续完善这个游戏,打算添加敌人,实现其智能寻找玩家并进行对抗。完成的效果如下: fps_enemy_demo 下载资源 首先是设计敌人,我们可以在网上找到一些好的免费素材,例如在Unity…...

STM32F103 CubeMX 使用USB生成鼠标设备
STM32F103 CubeMX 使用USB生成鼠标设备 1 配置cubeMX1.1配置外部晶振,配置debug口1.2 配置USB1.3 配置芯片的时钟1.4 生成工程 2. 编写代码2.1 添加申明2.2 main函数代码 1 配置cubeMX 1.1配置外部晶振,配置debug口 1.2 配置USB 1.3 配置芯片的时钟 需…...

HJXH-E1/U静态信号继电器 面板安装 辅助电源220VDC 启动电压220VDC JOSEF约瑟
HJXH系列静态信号继电器 HJXH-61/U静态信号继电器; HJXH-61/I静态信号继电器; HJXH-62/U静态信号继电器; HJXH-62/I静态信号继电器; HJXH-E1/U静态信号继电器; HJXH-E1/I静态信号继电器; HJXH-E2/U静态信号…...
SpringBoot3下Kafka分组均衡消费实现
首先添加maven依赖: <dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId><version>2.8.11</version><exclusions><!--此处一定要排除kafka-clients,然…...

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:GridItem)
网格容器中单项内容容器。 说明: 该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。仅支持作为Grid组件的子组件使用。 子组件 可以包含单个子组件。 接口 GridItem GridItem(value?: GridItemOptions)…...

Qt 使用RAW INPUT获取HID触摸屏,笔设备,鼠标的原始数据,最低受支持的客户端:Windows XP [仅限桌面应用]
在开发绘图应用程序时,经常会需要读取笔设备的数据,通过对笔数据的解析,来判断笔的坐标,粗细。如果仅仅只是读取鼠标的坐标,就需要人为在应用程序端去修改笔的粗细,并且使用体验不好,如果可以实…...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...