小红书网站建设目的/长春百度网站快速排名
目录
AQS底层源码深度剖析-Lock锁
ReentrantLock底层原理
为什么把获取锁失败的线程加入到阻塞队列中,而不是采取其它方法?
总结:三大核心原理
CAS是啥?
代码模拟一个CAS:
公平锁与非公平锁
可重入锁的应用场景:
AQS底层源码分析[重点,难点]
类图展示:
AQS类属性分析:
AQS类中Node内部节点类:
一:获取锁 lock()
概念:
源码深度剖析:
二:释放锁 unlock()
概念:
源码深度剖析:
三:中断interrupt() 与LockSupport.park(),LockSupport.park(Object obj)的关系【重点】
首先要明白以下三个API:
其次要明白:
提出问题:为什么需要中断方法interrupt()的调用?
代码示例1:原生park()和unpark()的使用
代码示例2:原生park()和unpark() 结合 interrupt相关的API 的使用
代码示例三:在代码示例二的基础上加上一个interrupted方法
重点: LockSupport.park(Object obj)结合AQS底层源码剖析:
得出结论:
四:lockInterruptibly()深度剖析
概念:
源码深度剖析:
总结:lock()与lockInterruptibly()里面中断(interrupt)信号的实际应用场景【重点】
lock():
lockInterruptibly():
AQS底层源码深度剖析-Lock锁
ReentrantLock底层原理
为什么把获取锁失败的线程加入到阻塞队列中,而不是采取其它方法?
1.sleep休眠不可取:因为获取锁的线程,执行业务多久是不确定的,所以获取锁失败的线程的休眠时间是不确定的。
2.让出CPU资源不可取:让给谁?
3.直接挂起不可取:直接挂起,等于说性能相当于重量级锁,把对未获取锁线程的管理的任务交给了操作系统,操作系统需要进行切换状态(从用户态切换回内核态),是极其耗费性能的。
所以,我们把未获取锁成功的线程加入到一个阻塞队列中,等到获取锁的线程执行完任务后可以去唤醒这些阻塞的线程。
总结:三大核心原理
自旋,LockSuport,CAS(高并发也可以保证原子性)
【 queue队列存储进入的线程,实现公平锁机制。】
分析:自旋就是一直while循环尝试获取锁。
LockSupport就是阻塞线程与唤醒线程,阻塞是为了降低自旋,以此降低CPU所消耗的性能。
CAS就是在多线程同时操作时,保证操作的原子性,同时一刻只能有一个线程修改主内存的数据成功。
CAS是啥?
代码模拟一个CAS:
/*** @Description: TODO* @Author: etcEriksen* @Date: 2023/3/5**/
@SuppressWarnings({"all"})
@Slf4j
public class Juc04_Thread_Cas {/*** state标识当前加锁状态,记录加锁的次数* 默认加锁次数为0*/private volatile int state = 0 ;private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5) ;private static Juc04_Thread_Cas cas = new Juc04_Thread_Cas() ;static class Worker implements Runnable {@Overridepublic void run() {log.info("请求:{}到达预定点,准备开始抢state:)",Thread.currentThread().getName());try {//阻塞所有的线程到这里,然后一起执行后面的逻辑操作cyclicBarrier.await();if (cas.compareAndSwapState(0,1)) {log.info("当前请求:{},抢到锁!",Thread.currentThread().getName());}else{log.info("当前请求:{},抢锁失败!",Thread.currentThread().getName());}} catch (Exception e) {e.printStackTrace();}}}public final boolean compareAndSwapState(int oldValue,int newValue) {/*** 比较stateOffset的值和oldValue值是否一致,若不一致,那么不可修改 。如果一致,那么修改值为:newValue。*/return unsafe.compareAndSwapInt(this,stateOffset,oldValue,newValue);}private static final Unsafe unsafe = UnsafeInstance.reflectGetUnsafe();private static final long stateOffset;static {try {stateOffset = unsafe.objectFieldOffset(Juc04_Thread_Cas.class.getDeclaredField("state"));} catch (Exception e) {throw new Error();}}
}
public class UnsafeInstance {public static Unsafe reflectGetUnsafe() {try {Field field = Unsafe.class.getDeclaredField("theUnsafe");field.setAccessible(true);return (Unsafe) field.get(null);} catch (Exception e) {e.printStackTrace();}return null;}public static void main(String[] args) {int j=1;reflectGetUnsafe().loadFence();int i= 0;}
}
公平锁与非公平锁
t-0正在CAS执行任务逻辑,当执行完的那一刻,t-4正好进入了,那么如果是非公平锁,那么t-4不用到阻塞队列的最后去排队,而是直接去抢t-0线程执行完任务后进行释放的锁对象。如果是公平锁,那么t-4就会进入阻塞队列,老老实实的排到队尾等待。
可重入锁的应用场景:
AQS底层源码分析[重点,难点]
类图展示:
AQS类属性分析:
注释:state为锁的状态器。eg:无锁状态时,state=0。同一线程每成功获取该锁一次,则state+1,同一线程每释放该锁对象一次,则state-1。
exclusiveOwnerThread: 表示当前持有锁对象的线程为谁
head和tail就是在维护一个双端队列,后面该队列用来存储获取锁失败的线程。
AQS类中Node内部节点类:
注释:prev和next:分别表示当前Node节点的前驱和后继节点
waitStatus:Node节点的生命状态,信号量。表示Node节点在不同生命状态下所呈现出的不同性质,有五种,如下:
SIGNAL = -1 //可被唤醒
CANCELLED = 1 //代表出现异常,中断引起的,需要废弃结束
CONDITION = -2 // 条件等待
PROPAGATE = -3 // 传播
0 - 初始状态Init状态
thread:由于我们是使用多个Node节点组成一个双端阻塞队列,队列中的每一个元素都是一个Node,每一个Node对应维护一个线程对象,所以我们需要在Node节点类中维护一个Thread类属性:thread 。
一:获取锁 lock()
概念:
这种lock()获取锁对象的方式必须等持有锁对象的线程做完事情,其他等待的线程才可以做事情。而且中途不能退出,会一直阻塞存储在CLH双端队列中。
说详细点就是:当一个节点对象加入到CLH双端阻塞队列中后,在被持有锁对象唤醒之前,它中途就不能退出该队列,即使阻塞队列中节点对应的线程对象被中断,也不可以退出!那么对队列中节点对应线程的中断有啥作用呢?最后的总结lock()和lockInterruptibly()会举例细说
源码深度剖析:
1.
如果锁未被另一个线程持有,则获取锁并立即返回,将锁保持计数设置为 1。
如果当前线程已持有锁,则保留计数将递增 1,并且该方法会立即返回。
如果锁由另一个线程持有,则当前线程将出于线程调度目的而被禁用,并处于休眠状态,直到获取锁为止,此时锁保持计数设置为 1。
2.
3.
4.
接下来会详细分析tryAcquire和acquireQueued这两个方法:
tryAcquire方法解析:
tryAcquire方法中调用的hasQueuePredecessors方法的解析:
acquireQueued方法解析:
acquireQueued调用的addWaiter方法解析:
addWaiter调用的enq()方法解析:
提出一个疑问和思考:
当队列初始化完毕时,为什么在addWaiter方法中已经做了插入新Node到队列中的操作,然而又在addWaiter调用的enq方法中又重复做了一次插入新Node节点到队列中的操作呢???
答案:因为并发多线程环境!
acquireQueued方法解析:
acquireQueued方法调用的shouldParkAfterFailedAcquire方法解析:
acquireQueued方法调用的parkAndCheckInterrupt方法解析:
为什么要把第一个真实节点的状态设置在虚拟头节点的waitStatus上?为什么不设计在自己节点所维护的waitStatus呢?
首先你应该明白一个点:我们是公平锁,所谓公平锁就是先来的先可以获取到锁对象,后来的只能在后面排队等。
所以我们构建出了一个有序的阻塞队列,后来的线程插在最后面,第一个真实节点(虚拟节点的下一个节点)就是下一次锁对象的持有者。
当锁对象释放后,每次都是把锁对象分配给第一个真实节点(即虚拟头节点的下一个节点),这就是保证了公平。
第一个真实节点获取到锁对象后,会转变为虚拟头节点(此时waitStatus==0),原来的虚拟头节点会被GC回收掉。此时只需把新虚拟节点的waitStatus改为-1即可,因为waitStatus维护的是Node节点的性质,所以最好维护在虚拟头节点。
为什么要费尽心思搞出一个waitStatus并且设置为-1?
因为waitStatus==-1就是代表一个Node节点的状态量,代表的是可以进行被唤醒,那么在释放锁的时候,我们只需进行检验该waitStatus是否为-1,为-1,那么才可以去释放锁 才可以去唤醒该线程对应的Node节点。
二:释放锁 unlock()
概念:
就是当前锁持有者对应的线程执行锁对象的释放并且通知唤醒阻塞队列中的第一个真实节点对应的线程对象(即虚拟头节点Head的下一个节点)。
在公平锁的实现下,释放的锁对象肯定是被排在阻塞队列中的第一个节点对应的线程获取。但是在非公平锁的实现下,释放的锁对象可能会被刚刚进入的线程竞争获取到,导致阻塞队列第一个节点对应的线程对象获取锁对象失败。
源码深度剖析:
1.
2.
3.release方法解析:
release方法调用的tryRelease方法解析:
release方法调用的unparkSuccessor方法解析:
unparkSuccessor方法执行唤醒线程的操作后,会唤醒加锁过程中调用的parkAndCheckInterrupt方法中阻塞的线程:
parkAndCheckInterrupt阻塞的线程被唤醒后,说明parkAndCheckInterrupt阻塞完毕 即执行完毕,那么再一次循环向上,如图分析:
为什么要在 unparkSuccessor 方法中把Head虚拟头节点的waitStatus从-1改为0??
其实也说的很清楚了。
重复一遍释放锁唤醒线程的逻辑:t1线程执行完业务后进行释放锁并且通知唤醒阻塞队列中阻塞着的t2线程,在唤醒之前,需要把阻塞队列虚拟头节点Head的waitStatus由-1改为0。
但是在非公平锁的情况下,可能阻塞队列的第一个Node对应的t2线程在获取锁的时候 有其它线程进入跟它竞争锁,可能t2线程获取锁失败。获取锁成功,那好说。
如果t2线程获取锁失败,t2线程会进行两次循环【第二次循环是逻辑关键,阻塞前最后一次尝试获取锁,若成功那么就无需阻塞啦,极大的节省性能】:
第一次循环:由于t2线程获取锁失败,那么执行的acquireQueued方法中执行调用tryAcquire方法返回false。t2线程会再一次进入到shouldParkAfterFailedAcquire方法中,由于Head的waitStatus被t1线程在释放锁的时候改为了0,所以shouldParkAfterFailedAcquire方法中会执行逻辑把waitStatus改为-1,并且返回false。
第二次循环:会在阻塞前再一次的进行尝试获取锁(tryAcquire),因为阻塞是重型操作,需要切换状态,所以阻塞之前再一次进行尝试获取锁(做最后一次尝试),如果获取锁还是失败,那么就进行阻塞,循环结束。
假设说,t1线程释放锁后,在唤醒t2之前,不把阻塞队列虚拟头节点Head的waitStatus由-1改为0。那么t2线程只会进行一次循环,那么就不可能完成"阻塞前最后一次tryAcquire尝试获取锁"的功能啦
三:中断interrupt() 与LockSupport.park(),LockSupport.park(Object obj)的关系【重点】
对于第三点,我们依旧结合AQS底层源码进行分析,前面一(获取锁)和二(释放锁)这两大方面分别代表了AQS源码的逻辑主线,第三点算是对前面两点中涉及到interrupt中断源码的分析与补充。
首先要明白以下三个API:
1.Thread.currentThread().interrupt(),表示为当前线程打一个中断标记,中断当前线程。
2.Thread.interrupted(),表示清除中断标记,如果当前线程被中断了,返回true。否则返回false
3.Thread.currentThread().isInterrupted()表示查看当前线程的状态是否中断,不清除中断标记
其次要明白:
对于AQS底层,我们调用unlock()方法的底层就是调用LockSupport.park(Object obj)的,
LockSupport.park()与LockSupport.park(Object obj)这两个方法还有所不同的。
LockSupport.park()方法:在执行park()方法之前先进行检验当前线程t1是否有中断标识,如果没有,那么标识着当前t1线程要被阻塞住,不能向下执行。
所谓中断标识就是:如果在其它线程中执行t1.interrupt()中断阻塞着的park方法,那么就会给当前线程t1分配一个中断标识,就会停止阻塞,向下继续执行。
但是当我们执行Thread.currentThread().interrupted()后就会消除该中断标识,那么当下一次park()执行前进行检验中断标识时 ,发现没有!那么就会继续阻塞住。
LockSupport.park(Object obj)方法:它与LockSupport.park()方法相同,obj标识阻塞等待的对象
提出问题:为什么需要中断方法interrupt()的调用?
分析:
interrupt方法为中断线程的方法api,可以中断线程的阻塞。即是中断LockSupport.park()对线程的阻塞,也可以中断Lock.park(Object obj)对线程的阻塞。
之前是如何中断线程的?使用的是底层的stop方法,就好比linux系统下的kill -9 操作,无论是jdk提供的stop还是linux的kill,都是极其的暴力的,会立刻杀死当前线程(线程是进程的组成部分),假设业务执行一半被强制杀死了,那么就是十分危险的。
所以我们寻求一种柔和的中断方式,可以给我们判断的机会来自定义何时进行退出,以此保证业务的安全退出与执行,因此引出interrupt方法+isInterrupted方法来保证自定义退出逻辑:
首先:使用interrupt()方法进行中断阻塞后,会给中断阻塞的线程一个中断标识,标识该线程已经中断啦。
然后:我们就可以结合Thread.currentThread().isInterrupted()方法进行查看当前线程是否已经中断,如果别处已经把该线程中断(即调用interrupt()方法),那么我们就要执行退出。如果判断出该线程未中断,那么继续执行业务。
代码示例1:原生park()和unpark()的使用
/*** @Description: TODO* @Author: etcEriksen* @Date: 2023/3/6**/
@SuppressWarnings({"all"})
@Slf4j
public class demo2 {public static void main(String[] args) {Thread t1 = new Thread(() -> {while (true) {log.info("开始阻塞");LockSupport.park();log.info("阻塞结束");log.info("若不中断,unpark执行完,那么执行业务逻辑");}});t1.start();try {Thread.sleep(2000);log.info("中断线程");LockSupport.unpark(t1);} catch (InterruptedException e) {e.printStackTrace();}log.info("main线程执行结束");}}
代码分析:
比对运行结果,与分析一致:
代码示例2:原生park()和unpark() 结合 interrupt相关的API 的使用
记住两个点:
(1) interrupt()方法是给线程一个标记:你这个线程已经被附上一个阻断标识了啊,你再park()阻塞也没用,不会阻塞的哈!
(2) interrupt()进行中断后,会给线程做一个中断标识,我们可以调用isInterrupted这个方法去自定义自己的逻辑:中断后去做啥逻辑,退出?还是执行其它业务?这些都由你自己决定!再一次验证了前面的结论:这是一个自定义的,是柔和的中断。
/*** @Description: TODO* @Author: etcEriksen* @Date: 2023/3/6**/
@SuppressWarnings({"all"})
@Slf4j
public class demo2 {public static void main(String[] args) {Thread t1 = new Thread(() -> {while (true) {log.info("开始阻塞");LockSupport.park();log.info("阻塞结束");if (Thread.currentThread().isInterrupted()) {//再一次执行park()是为了表明:只要该线程被标识了中断标记,那么park()执行多少次也不会阻塞的!LockSupport.park();log.info("中断退出,不执行后面的业务逻辑");}log.info("若不中断,unpark执行完,那么执行业务逻辑");}});t1.start();try {Thread.sleep(2000);log.info("中断线程");t1.interrupt();//LockSupport.unpark(t1);} catch (InterruptedException e) {e.printStackTrace();}log.info("main线程执行结束");}}
代码分析:
代码运行结果:一直死循环打印输出,没有尽头
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 若不中断,unpark执行完,那么执行业务逻辑
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 开始阻塞
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 阻塞结束
17:19:15.853 [Thread-0] INFO com.messi.leo.AQS.lock.demo2 - 中断退出,不执行后面的业务逻辑
代码示例三:在代码示例二的基础上加上一个interrupted方法
记住一个点:
interrupted方法就是消除当前调用方法的线程对象的中断标识。那么当线程的中断标识被消除成功时,我们再一次park(),线程就会被阻塞掉
返回值为布尔类型,若当前线程有中断标识,那么返回true。否则返回false
代码演示:
/*** @Description: TODO* @Author: etcEriksen* @Date: 2023/3/6**/
@SuppressWarnings({"all"})
@Slf4j
public class demo2 {public static void main(String[] args) {Thread t1 = new Thread(() -> {while (true) {log.info("开始阻塞");LockSupport.park();log.info("阻塞结束");if (Thread.currentThread().isInterrupted()) {boolean isInterrupted = Thread.currentThread().interrupted();if (isInterrupted) {log.info("线程的中断标识被消除成功,再一次park()执行时 线程就会被阻塞掉");}LockSupport.park();log.info("中断退出,不执行后面的业务逻辑");}log.info("若不中断,unpark执行完,那么执行业务逻辑");}});t1.start();try {Thread.sleep(2000);log.info("中断线程");t1.interrupt();//LockSupport.unpark(t1);} catch (InterruptedException e) {e.printStackTrace();}log.info("main线程执行结束");}}
代码分析:
代码输出:
重点: LockSupport.park(Object obj)结合AQS底层源码剖析:
ReentrantLock类对应的加锁流程:
1.
2.
3.
4.
5.
6.
7.
ReentrantLock类对应的释放锁流程:
1.
2.
得出结论:
unpark和interrupt方法都可以进行停止park方法的线程阻塞。
但是interrupt不同之处在于,它给线程对象分配了一个中断标识,当下一次进行park时判断出当前线程对象存在该中断标识,那么就不会park阻塞!!!因此我们可以使用interrupted方法进行消除线程对象的中断标识!
四:lockInterruptibly()深度剖析
概念:
lockInterruptibly()方法和lock方法一致,也必须是等待持有锁对象的线程做完事情,其他线程才能做事情,但中途可以进行退出。
说详细点就是:当节点(线程封装在节点中)加入到阻塞队列中后,若线程执行interrupt中断方法,会抛出中断异常,中途会从阻塞队列中进行退出,表示获取锁失败,waitStatus标记为1,表示出现异常导致退出!
源码深度剖析:
1.
2.
3.
4.
doAcquireInterruptibly方法调用的shouldParkAfterFailedAcquire方法解析:与lock()底层的shouldParkAfterFailedAcquire方法一致,不再详细赘述
doAcquireInterruptibly方法调用的 parkAndCheckInterrupt方法 解析:
5.
6.cacelAcquire方法解析:
至于这个方法的具体实现,总结一句话就是对阻塞队列中出现异常的节点要进行从阻塞队列中移除,并且抛出中断异常,并且要进行修改节点对应waitStatus的值。具体怎么实现的,画一张图,一步步对照着走即可,easy!
总结:lock()与lockInterruptibly()里面中断(interrupt)信号的实际应用场景【重点】
lock():
概念回顾
这种lock()获取锁对象的方式必须等持有锁对象的线程做完事情,其他等待的线程才可以做事情。而且中途不能退出,会一直阻塞存储在CLH双端队列中。
说详细点就是:当一个节点对象加入到CLH双端阻塞队列中后,在被持有锁对象唤醒之前,它中途就不能退出该队列,即使阻塞队列中节点对应的线程对象被中断,也不可以退出!
lockI()底层的CLH阻塞队列中Node节点对应的线程对应的中断信号有啥用?
实际案例证明:还是追源码,中间省略了很多easy代码,只保留了关键处
selfInterrupt方法对当前线程的中断有啥用?我在搞笑吗???
回到API调用的应用层:
如下图:画红圈就是作用,我们可以跳过interrupted()方法得到中断的信号去自定义业务逻辑!!!!很重要的一个点!
lockInterruptibly():
概念回顾:
方法和lock方法一致,也必须是等待持有锁对象的线程做完事情,其他线程才能做事情,但中途可以进行退出。
说详细点就是:当节点(线程封装在节点中)加入到阻塞队列中后,若线程执行interrupt中断方法,会抛出中断异常,中途会从阻塞队列中进行退出,表示获取锁失败,waitStatus标记为1,表示出现异常导致退出!
lockInterruptibly()底层的CLH阻塞队列中Node节点对应的线程对应的中断信号有啥用?
实际案例证明:还是追源码,中间省略了很多easy代码,只保留了关键处:
回到API调用的应用层:
初次学习AQS底层源码,可能理解不深刻。若有不对,期待指正。
相关文章:

AQS底层源码深度剖析-Lock锁
目录 AQS底层源码深度剖析-Lock锁 ReentrantLock底层原理 为什么把获取锁失败的线程加入到阻塞队列中,而不是采取其它方法? 总结:三大核心原理 CAS是啥? 代码模拟一个CAS: 公平锁与非公平锁 可重入锁的应用场景&…...

网络编程(二)
6. TCP 三次握手四次挥手 HTTP 协议是 Hype Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web)服务器(sever)传输超文本到客户端(本地浏览器…...

访问学者进入美国哪些东西不能带?
随着疫情的稳定,各国签证的逐步放开,成功申请到国外访问学者、博士后如何顺利的进入国外,哪些东西不能带,下面就随知识人网小编一起看一看。一、畜禽肉类(Meats, Livestock and Poultry)不论是新鲜的、干燥的、罐头的、真空包装的…...

灵巧手抓持<分类><仿真>
获取灵巧手抓取物体时的抓持类型,需要考虑:手本身的结构、被抓取物体的形状尺寸、抓持操作任务的条件。 研究方法:基于模型的方法、基于数据驱动的方法 基于模型的方法:建立灵巧手抓持相关的运动学和动力学模型建立目标函数求解…...

CENTO OS上的网络安全工具(十九)ClickHouse集群部署
一、VMware上集群部署ClickHouse (一)网络设置 1. 通过修改文件设置网络参数 (1)CentOS 在CENTOS上的网络安全工具(十六)容器特色的Linux操作_lhyzws的博客-CSDN博客中我们提到过可以使用更改配置文件的方式…...

tesseract -图像识别
下载链接:https://digi.bib.uni-mannheim.de/tesseract/如下选择最新的版本,这里我选择tesseract-ocr-w64-setup-5.3.0.20221222.exe有如下python模块操作tesseractpyocr 国内源:pip install -i https://pypi.mirrors.ustc.edu.cn/simple/ py…...

JavaScript Math 算数对象
文章目录JavaScript Math 算数对象Math 对象Math 对象属性Math 对象方法算数值算数方法JavaScript Math 算数对象 Math(算数)对象的作用是:执行常见的算数任务。 Math 对象 Math(算数)对象的作用是:执行普…...

一体机HDATA节点添加和删除
瀚高数据库 目录 文档用途 详细信息 文档用途 一体机可在线添加、删除数据库集群节点。 详细信息 一体机可在线添加、删除数据库集群节点。具体操作步骤如下 一、节点添加 集群可以在其他机器上通过配置hghac.yaml文件,将新节点加入集群。 集群操作 1…...

关于 interface{} 会有啥注意事项?上
学习 golang ,对于 interface{} 接口类型,我们一定绕不过,咱们一起来看看 使用 interface{} 的时候,都有哪些注意事项吧 interface {} 可以用于模拟多态 xdm 咱们写一个简单的例子,就举动物的例子 写一个 Animal 的…...

Matlab中旧版modem.qammod与新版不兼容
最近,因为课题需要,在研究通信。在网上下了一个2015年左右的代码,其中用的是matlab旧版中的modem.qammod函数,但是旧版中的函数已经被删除了,(这里必须得吐槽一下,直接该函数内部运行机制就行呀…...

通达信指标公式颜色代码的四种写法(COLOR/RGB)
通达信指标公式颜色代码有四种写法,分别为COLOR颜色的英文、COLOR十六进制、RGBX十六进制、RGB(R,G,B)。标题有点尴尬,让我想到孔乙己“茴”字的四种写法,哈哈。 一、COLOR颜色的英文 “COLOR颜色的英文”这种写法比较简单,函数库…...

小程序面试题收集(持续更新中...)
小程序面试题收集 1.请谈谈微信小程序主要目录和文件的作用 project.config.json:项目配置文件,用的最多的就是配置是否开启https校验App.js:设置一些全局的基础数据等App.json:底部tab,标题栏和路由等设置App.wxss&…...

最深情的告白——郁金香(Python实现)
目录 1 最深情的告白 2 即兴赞之 2.1 李小白言郁金香 2.2 郁金香般的姑娘 2.3 荷兰的郁金香 3 Python代码实现 3.1 郁金香的芬芳 3.2 我俩绚丽多姿的风景 1 最深情的告白 曾经以为,她爱玫瑰,然后我画了好几种: 花仙子——玫瑰&a…...

代码随想录算法训练营第六天|242.有效的字母异位词 、349. 两个数组的交集 、 202. 快乐数、1. 两数之和
当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。哈希法是牺牲了空间换取了时间,要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。当我们要使用集合来解决哈希问题的时候,优先使用…...

【STL】模拟实现list
目录 1、list介绍 所要实现类及其成员函数接口总览 2、结点类的模拟实现 基本框架 构造函数 3、迭代器类的模拟实现 迭代器类存在的意义 3.1、正向迭代器 基本框架 默认成员函数 构造函数 运算符重载 --运算符重载 !运算符重载 运算符重载 *运算符重载 …...

Spring Cloud Alibaba全家桶(五)——微服务组件Nacos配置中心
前言 本文小新为大家带来 微服务组件Nacos配置中心 相关知识,具体内容包括Nacos Config快速开始指引,搭建nacos-config服务,Config相关配置,配置的优先级,RefreshScope注解等进行详尽介绍~ 不积跬步,无以至…...

【微信小程序】-- 页面事件 - 下拉刷新(二十五)
💌 所属专栏:【微信小程序开发教程】 😀 作 者:我是夜阑的狗🐶 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询! &…...

springboot启动过程加载数据笔记(springboot3)
SpringApplication AbstractApplicationContext PostProcessorRegistrationDelegate ConfigurationClassPostProcessor ConfigurationClassParser 一堆循环和调用 ComponentScanAnnotationParser扫描 processConfigurationClass.doProcessConfigurationClass(configClass, so…...

中文代码86
PK 嘚釦 docProps/PK 嘚釦諿A眎 { docProps/app.xml漅薾?糤?D?v拢W4揣狤"攃e9 睔貣m*:PAz韒g?项弇}R珁湧4嶱 ]I禑菦?櫮戵\U佳 珩 ]铒e礎??X(7弅锿?jl筀儸偛佣??z窊梈ZT炰攷 ?\ 銒沆?状尧绥>蕮 ?斬殕{do]?o乗YX?:??罢秗,泿)怟 …...

网络参考模型
OSI参考模型 应用层 不服务于任何其他层,就是位APP提供相应的服务,不如HTTP、域名解析DNS提供服务表示层 1.使得应用数据能够被不同的系统(Windows\Linux)进行识别和理解 2.数据的解码和编码、数据的加密与解密、数据的压缩和解…...

Spark Tungsten
Spark Tungsten数据结构Unsafe Row内存页管理全阶段代码生成火山迭代模型WSCG运行时动态生成Tungsten (钨丝计划) : 围绕内核引擎的改进: 数据结构设计全阶段代码生成(WSCG,Whole Stage Code Generation) 数据结构 Tungsten 在…...

2023年总结的web前端学习路线分享(学习导读)
如果你打开了这篇文章,说明你是有兴趣想了解前端的这个行业的,以下是博主2023年总结的一些web前端的学习分享路线,如果你也想从事前端或者有这方面的想法的,请接着往下看! 前端发展前景 前端入门 巩固基础 前端工程…...

MyBatis学习笔记(十) —— 动态SQL
10、动态SQL MyBatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串的痛点问题。 动态SQL: 1、if 标签:通过test属性中的表达式判断标签中的内容是否有效(是否会拼接到sql中…...

剑指 Offer 55 - II. 平衡二叉树
剑指 Offer 55 - II. 平衡二叉树 难度:easy\color{Green}{easy}easy 题目描述 输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。 示例 1: 给定二叉树 […...

一文吃透前端低代码的 “神仙生活”
今天来说说前端低代码有多幸福? 低代码是啥?顾名思义少写代码…… 这种情况下带来的幸福有:代码写得少,bug也就越少(所谓“少做少错”),因此开发环节的两大支柱性工作“赶需求”和“修bug”就…...

【深度学习】预训练语言模型-BERT
1.BERT简介 BERT是一种预训练语言模型(pre-trained language model, PLM),其全称是Bidirectional Encoder Representations from Transformers。下面从语言模型和预训练开始展开对预训练语言模型BERT的介绍。 1-1 语言模型 语言模型 …...

C++类的组合
C类的组合什么是类的组合初始化参数列表使用类的组合案例分析组合构造和析构顺序问题this指针基本用法和作用什么是类的组合 类的组合就是以另一个对象为数据成员,这种情况称为类的组合 1.优先使用类的组合,而不是继承 2.组合表达式的含义 一部分关系 初…...

2.伪随机数生成器(ctr_drbg)的配置与使用
零、随机数应用 生成盐,用于基于口令的密码 生成密钥,用于加密和认证 生成一次性整数Nonce,防止重放攻击 生成初始化向量IV 构成 种子,真随机数生成器的种子来源于物理现象 内部状态,种子用来初始化内部状态 一、真随机数和伪随机数 1.区别 随机数在安全技术中通常被用于…...

CentOS7 切换图形模式和多用户命令行模式
备注: 主机名 hw 含义:hardware 缩写,意思是硬件(物理机) 文章目录1、查看源头2、查看当前系统运行模式3、设置系统运行模式为多用户命令行模式4、查看当前系统运行模式5、重启系统6、确认当前系统运行模式7、设置系统…...

在linux上用SDKMan对Java进行多版本管理
在linux上用SDKMan对Java进行多版本管理 有一个工具叫SDKMan,它允许我们这样做。官方网站这样描述: TIP: "SDKMan 是一个工具,用于在大多数基于Unix的系统上管理多个软件开发工具包的并行版本。它提供了一个方便的命令行接口(CLI)和API,…...