ReentrantLock源码详解--公平锁、非公平锁

问题

(1)重入锁是什么?

(2)ReentrantLock如何实现重入锁?

(3)ReentrantLock为什么默认是非公平模式?

(4)ReentrantLock除了可重入还有哪些特性?

简介

Reentrant = Re + entrant,Re是重复、又、再的意思,entrant是enter的名词或者形容词形式,翻译为进入者或者可进入的,所以Reentrant翻译为可重复进入的、可再次进入的,因此ReentrantLock翻译为重入锁或者再入锁。

重入锁,是指一个线程获取锁之后再尝试获取锁时会自动获取锁。

在Java中,除了ReentrantLock以外,synchronized也是重入锁。

那么,ReentrantLock的可重入性是怎么实现的呢?

继承体系

ReentrantLock实现了Lock接口,Lock接口里面定义了java中锁应该实现的几个方法:

// 获取锁
void lock();
// 获取锁(可中断)
void lockInterruptibly() throws InterruptedException;
// 尝试获取锁,如果没获取到锁,就返回false
boolean tryLock();
// 尝试获取锁,如果没获取到锁,就等待一段时间,这段时间内还没获取到锁就返回false
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 释放锁
void unlock();
// 条件锁
Condition newCondition();

Lock接口中主要定义了 获取锁、尝试获取锁、释放锁、条件锁等几个方法。

源码分析

主要内部类

ReentrantLock中主要定义了三个内部类:Sync、NonfairSync、FairSync。

abstract static class Sync extends AbstractQueuedSynchronizer {}
static final class NonfairSync extends Sync {}
static final class FairSync extends Sync {}

(1)抽象类Sync实现了AQS的部分方法;

(2)NonfairSync实现了Sync,主要用于非公平锁的获取;

(3)FairSync实现了Sync,主要用于公平锁的获取。

在这里我们先不急着看每个类具体的代码,等下面学习具体的功能点的时候再把所有方法串起来。

主要属性

private final Sync sync;

主要属性就一个sync,它在构造方法中初始化,决定使用公平锁还是非公平锁的方式获取锁。

主要构造方法

// 默认构造方法
public ReentrantLock() {
sync = new NonfairSync();
}
// 自己可选择使用公平锁还是非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}

(1)默认构造方法使用的是非公平锁;

(2)第二个构造方法可以自己决定使用公平锁还是非公平锁;

上面我们分析了ReentrantLock的主要结构,下面我们跟着几个主要方法来看源码。

lock()方法

彤哥贴心地在每个方法的注释都加上方法的来源。

1.公平锁

这里我们假设ReentrantLock的实例是通过以下方式获得的:

ReentrantLock reentrantLock = new ReentrantLock(true);

下面的是加锁的主要逻辑:

// ReentrantLock.lock()
public void lock() {
// 调用的sync属性的lock()方法
// 这里的sync是公平锁,所以是FairSync的实例
sync.lock();
}
// ReentrantLock.FairSync.lock()
final void lock() {
// 调用AQS的acquire()方法获取锁
// 注意,这里传的值为1
acquire(1);
}
// AbstractQueuedSynchronizer.acquire()
public final void acquire(int arg) {
// 尝试获取锁
// 如果失败了,就排队
if (!tryAcquire(arg) &&
// 注意addWaiter()这里传入的节点模式为独占模式
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// ReentrantLock.FairSync.tryAcquire()
protected final boolean tryAcquire(int acquires) {
// 当前线程
final Thread current = Thread.currentThread();
// 查看当前状态变量的值
int c = getState();
// 如果状态变量的值为0,说明暂时还没有人占有锁
if (c == 0) {
// 如果没有其它线程在排队,那么当前线程尝试更新state的值为1
// 如果成功了,则说明当前线程获取了锁
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
// 当前线程获取了锁,把自己设置到exclusiveOwnerThread变量中
// exclusiveOwnerThread是AQS的父类AbstractOwnableSynchronizer中提供的变量
setExclusiveOwnerThread(current);
// 返回true说明成功获取了锁
return true;
}
}
// 如果当前线程本身就占有着锁,现在又尝试获取锁
// 那么,直接让它获取锁并返回true
else if (current == getExclusiveOwnerThread()) {
// 状态变量state的值加1
int nextc = c + acquires;
// 如果溢出了,则报错
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
// 设置到state中
// 这里不需要CAS更新state
// 因为当前线程占有着锁,其它线程只会CAS把state从0更新成1,是不会成功的
// 所以不存在竞争,自然不需要使用CAS来更新
setState(nextc);
// 当线程获取锁成功
return true;
}
// 当前线程尝试获取锁失败
return false;
}
// AbstractQueuedSynchronizer.addWaiter()
// 调用这个方法,说明上面尝试获取锁失败了
private Node addWaiter(Node mode) {
// 新建一个节点
Node node = new Node(Thread.currentThread(), mode);
// 这里先尝试把新节点加到尾节点后面
// 如果成功了就返回新节点
// 如果没成功再调用enq()方法不断尝试
Node pred = tail;
// 如果尾节点不为空
if (pred != null) {
// 设置新节点的前置节点为现在的尾节点
node.prev = pred;
// CAS更新尾节点为新节点
if (compareAndSetTail(pred, node)) {
// 如果成功了,把旧尾节点的下一个节点指向新节点
pred.next = node;
// 并返回新节点
return node;
}
}
// 如果上面尝试入队新节点没成功,调用enq()处理
enq(node);
return node;
}
// AbstractQueuedSynchronizer.enq()
private Node enq(final Node node) {
// 自旋,不断尝试
for (;;) {
Node t = tail;
// 如果尾节点为空,说明还未初始化
if (t == null) { // Must initialize
// 初始化头节点和尾节点
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 如果尾节点不为空
// 设置新节点的前一个节点为现在的尾节点
node.prev = t;
// CAS更新尾节点为新节点
if (compareAndSetTail(t, node)) {
// 成功了,则设置旧尾节点的下一个节点为新节点
t.next = node;
// 并返回旧尾节点
return t;
}
}
}
}
// AbstractQueuedSynchronizer.acquireQueued()
// 调用上面的addWaiter()方法使得新节点已经成功入队了
// 这个方法是尝试让当前节点来获取锁的
final boolean acquireQueued(final Node node, int arg) {
// 失败标记
boolean failed = true;
try {
// 中断标记
boolean interrupted = false;
// 自旋
for (;;) {
// 当前节点的前一个节点
final Node p = node.predecessor();
// 如果当前节点的前一个节点为head节点,则说明轮到自己获取锁了
// 调用ReentrantLock.FairSync.tryAcquire()方法再次尝试获取锁
if (p == head && tryAcquire(arg)) {
// 尝试获取锁成功
// 这里同时只会有一个线程在执行,所以不需要用CAS更新
// 把当前节点设置为新的头节点
setHead(node);
// 并把上一个节点从链表中删除
p.next = null; // help GC
// 未失败
failed = false;
return interrupted;
}
// 是否需要阻塞
if (shouldParkAfterFailedAcquire(p, node) &&
// 真正阻塞的方法
parkAndCheckInterrupt())
// 如果中断了
interrupted = true;
}
} finally {
// 如果失败了
if (failed)
// 取消获取锁
cancelAcquire(node);
}
}
// AbstractQueuedSynchronizer.shouldParkAfterFailedAcquire()
// 这个方法是在上面的for()循环里面调用的
// 第一次调用会把前一个节点的等待状态设置为SIGNAL,并返回false
// 第二次调用才会返回true
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 上一个节点的等待状态
// 注意Node的waitStatus字段我们在上面创建Node的时候并没有指定
// 也就是说使用的是默认值0
// 这里把各种等待状态再贴出来
//static final int CANCELLED = 1;
//static final int SIGNAL = -1;
//static final int CONDITION = -2;
//static final int PROPAGATE = -3;
int ws = pred.waitStatus;
// 如果等待状态为SIGNAL(等待唤醒),直接返回true
if (ws == Node.SIGNAL)
return true;
// 如果前一个节点的状态大于0,也就是已取消状态
if (ws > 0) {
// 把前面所有取消状态的节点都从链表中删除
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
// 如果前一个节点的状态小于等于0,则把其状态设置为等待唤醒
// 这里可以简单地理解为把初始状态0设置为SIGNAL
// CONDITION是条件锁的时候使用的
// PROPAGATE是共享锁使用的
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
// AbstractQueuedSynchronizer.parkAndCheckInterrupt()
private final boolean parkAndCheckInterrupt() {
// 阻塞当前线程
// 底层调用的是Unsafe的park()方法
LockSupport.park(this);
// 返回是否已中断
return Thread.interrupted();
}

下面我们看一下主要方法的调用关系,可以跟着我的 → 层级在脑海中大概过一遍每个方法的主要代码:

ReentrantLock#lock()
->ReentrantLock.FairSync#lock() // 公平模式获取锁
->AbstractQueuedSynchronizer#acquire() // AQS的获取锁方法
->ReentrantLock.FairSync#tryAcquire() // 尝试获取锁
->AbstractQueuedSynchronizer#addWaiter() // 添加到队列
->AbstractQueuedSynchronizer#enq() // 入队
->AbstractQueuedSynchronizer#acquireQueued() // 里面有个for()循环,唤醒后再次尝试获取锁
->AbstractQueuedSynchronizer#shouldParkAfterFailedAcquire() // 检查是否要阻塞
->AbstractQueuedSynchronizer#parkAndCheckInterrupt() // 真正阻塞的地方

获取锁的主要过程大致如下:

(1)尝试获取锁,如果获取到了就直接返回了;

(2)尝试获取锁失败,再调用addWaiter()构建新节点并把新节点入队;

(3)然后调用acquireQueued()再次尝试获取锁,如果成功了,直接返回;

(4)如果再次失败,再调用shouldParkAfterFailedAcquire()将节点的等待状态置为等待唤醒(SIGNAL);

(5)调用parkAndCheckInterrupt()阻塞当前线程;

(6)如果被唤醒了,会继续在acquireQueued()的for()循环再次尝试获取锁,如果成功了就返回;

(7)如果不成功,再次阻塞,重复(3)(4)(5)直到成功获取到锁。

以上就是整个公平锁获取锁的过程,下面我们看看非公平锁是怎么获取锁的。

2.非公平锁

// ReentrantLock.lock()
public void lock() {
sync.lock();
}
// ReentrantLock.NonfairSync.lock()
// 这个方法在公平锁模式下是直接调用的acquire(1);
final void lock() {
// 直接尝试CAS更新状态变量
if (compareAndSetState(0, 1))
// 如果更新成功,说明获取到锁,把当前线程设为独占线程
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
// ReentrantLock.NonfairSync.tryAcquire()
protected final boolean tryAcquire(int acquires) {
// 调用父类的方法
return nonfairTryAcquire(acquires);
}
// ReentrantLock.Sync.nonfairTryAcquire()
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 如果状态变量的值为0,再次尝试CAS更新状态变量的值
// 相对于公平锁模式少了!hasQueuedPredecessors()条件
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}

相对于公平锁,非公平锁加锁的过程主要有两点不同:

(1)一开始就尝试CAS更新状态变量state的值,如果成功了就获取到锁了;

(2)在tryAcquire()的时候没有检查是否前面有排队的线程,直接上去获取锁才不管别人有没有排队呢;

总的来说,相对于公平锁,非公平锁在一开始就多了两次直接尝试获取锁的过程。

lockInterruptibly()方法

支持线程中断,它与lock()方法的主要区别在于lockInterruptibly()获取锁的时候如果线程中断了,会抛出一个异常,而lock()不会管线程是否中断都会一直尝试获取锁,获取锁之后把自己标记为已中断,继续执行自己的逻辑,后面也会正常释放锁。

题外话:

线程中断,只是在线程上打一个中断标志,并不会对运行中的线程有什么影响,具体需要根据这个中断标志干些什么,用户自己去决定。

比如,如果用户在调用lock()获取锁后,发现线程中断了,就直接返回了,而导致没有释放锁,这也是允许的,但是会导致这个锁一直得不到释放,就出现了死锁。

lock.lock();
if (Thread.currentThread().interrupted()) {
return ;
}
lock.unlock();

当然,这里只是举个例子,实际使用肯定是要把lock.lock()后面的代码都放在try...finally...里面的以保证锁始终会释放,这里主要是为了说明线程中断只是一个标志,至于要做什么完全由用户自己决定。

tryLock()方法

尝试获取一次锁,成功了就返回true,没成功就返回false,不会继续尝试。

// ReentrantLock.tryLock()
public boolean tryLock() {
// 直接调用Sync的nonfairTryAcquire()方法
return sync.nonfairTryAcquire(1);
}
// ReentrantLock.Sync.nonfairTryAcquire()
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}

tryLock()方法比较简单,直接以非公平的模式去尝试获取一次锁,获取到了或者锁本来就是当前线程占有着就返回true,否则返回false。

tryLock(long time, TimeUnit unit)方法

尝试获取锁,并等待一段时间,如果在这段时间内都没有获取到锁,就返回false。

// ReentrantLock.tryLock()
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
// 调用AQS中的方法
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
// AbstractQueuedSynchronizer.tryAcquireNanos()
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
// 如果线程中断了,抛出异常
if (Thread.interrupted())
throw new InterruptedException();
// 先尝试获取一次锁
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
// AbstractQueuedSynchronizer.doAcquireNanos()
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
// 如果时间已经到期了,直接返回false
if (nanosTimeout <= 0L)
return false;
// 到期时间
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return true;
}
nanosTimeout = deadline - System.nanoTime();
// 如果到期了,就直接返回false
if (nanosTimeout <= 0L)
return false;
// spinForTimeoutThreshold = 1000L;
// 只有到期时间大于1000纳秒,才阻塞
// 小于等于1000纳秒,直接自旋解决就得了
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
// 阻塞一段时间
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}

tryLock(long time, TimeUnit unit)方法在阻塞的时候加上阻塞时间,并且会随时检查是否到期,只要到期了没获取到锁就返回false。

unlock()方法

释放锁。

// java.util.concurrent.locks.ReentrantLock.unlock()
public void unlock() {
sync.release(1);
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer.release
public final boolean release(int arg) {
// 调用AQS实现类的tryRelease()方法释放锁
if (tryRelease(arg)) {
Node h = head;
// 如果头节点不为空,且等待状态不是0,就唤醒下一个节点
// 还记得waitStatus吗?
// 在每个节点阻塞之前会把其上一个节点的等待状态设为SIGNAL(-1)
// 所以,SIGNAL的准确理解应该是唤醒下一个等待的线程
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
// java.util.concurrent.locks.ReentrantLock.Sync.tryRelease
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
// 如果当前线程不是占有着锁的线程,抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 如果状态变量的值为0了,说明完全释放了锁
// 这也就是为什么重入锁调用了多少次lock()就要调用多少次unlock()的原因
// 如果不这样做,会导致锁不会完全释放,别的线程永远无法获取到锁
if (c == 0) {
free = true;
// 清空占有线程
setExclusiveOwnerThread(null);
}
// 设置状态变量的值
setState(c);
return free;
}
private void unparkSuccessor(Node node) {
// 注意,这里的node是头节点

// 如果头节点的等待状态小于0,就把它设置为0
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
// 头节点的下一个节点
Node s = node.next;
// 如果下一个节点为空,或者其等待状态大于0(实际为已取消)
if (s == null || s.waitStatus > 0) {
s = null;
// 从尾节点向前遍历取到队列最前面的那个状态不是已取消状态的节点
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// 如果下一个节点不为空,则唤醒它
if (s != null)
LockSupport.unpark(s.thread);
}

释放锁的过程大致为:

(1)将state的值减1;

(2)如果state减到了0,说明已经完全释放锁了,唤醒下一个等待着的节点;

彩蛋

为什么ReentrantLock默认采用的是非公平模式?

答:因为非公平模式效率比较高。

为什么非公平模式效率比较高?

答:因为非公平模式会在一开始就尝试两次获取锁,如果当时正好state的值为0,它就会成功获取到锁,少了排队导致的阻塞/唤醒过程,并且减少了线程频繁的切换带来的性能损耗。

非公平模式有什么弊端?

答:非公平模式有可能会导致一开始排队的线程一直获取不到锁,导致线程饿死。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • ReentrantLock源码详解--条件锁

    问题 (1)条件锁是什么? (2)条件锁适用于什么场景? (3)条件锁的await()是在其它线程signal()的时候唤醒的吗? 简介 条件锁,是指在获取锁之后发现当前业务场景自己无法处理,而需要等待某个条件的出现才可以继续处理时使用的一种锁. 比如,在阻塞队列中,当队列中没有元素的时候是无法弹出一个元素的,这时候就需要阻塞在条件notEmpty上,等待其它线程往里面放入一个元素后,唤醒这个条件notEmpty,当前线程才可以继续去做"弹出一个元素"的行为. 注意,这里的条件,必须是

  • java ReentrantLock详解

    介绍 ReentrantLock称为重入锁,比内部锁synchonized拥有更强大的功能,它可中断.可定时.设置公平锁 [注]使用ReentrantLock时,一定要释放锁,一般释放放到finnal里写. 提供以下重要的方法 lock():获得锁,如果锁已被占用,则等待 lockInterruptibly():获得锁,但有限响应中断 unlock():释放锁 tryLock():尝试获取锁.如果获得,返回true:否则返回false tryLock(long time, TimeUnit un

  • 详解java并发之重入锁-ReentrantLock

    前言 目前主流的锁有两种,一种是synchronized,另一种就是ReentrantLock,JDK优化到现在目前为止synchronized的性能已经和重入锁不分伯仲了,但是重入锁的功能和灵活性要比这个关键字多的多,所以重入锁是可以完全替代synchronized关键字的.下面就来介绍这个重入锁. 正文 ReentrantLock重入锁是Lock接口里最重要的实现,也是在实际开发中应用最多的一个,我这篇文章更接近实际开发的应用场景,为开发者提供直接上手应用.所以不是所有方法我都讲解,有些冷门

  • Java源码解析之可重入锁ReentrantLock

    本文基于jdk1.8进行分析. ReentrantLock是一个可重入锁,在ConcurrentHashMap中使用了ReentrantLock. 首先看一下源码中对ReentrantLock的介绍.如下图.ReentrantLock是一个可重入的排他锁,它和synchronized的方法和代码有着相同的行为和语义,但有更多的功能.ReentrantLock是被最后一个成功lock锁并且还没有unlock的线程拥有着.如果锁没有被别的线程拥有,那么一个线程调用lock方法,就会成功获取锁并返回.

  • ReentrantLock源码详解--公平锁、非公平锁

    问题 (1)重入锁是什么? (2)ReentrantLock如何实现重入锁? (3)ReentrantLock为什么默认是非公平模式? (4)ReentrantLock除了可重入还有哪些特性? 简介 Reentrant = Re + entrant,Re是重复.又.再的意思,entrant是enter的名词或者形容词形式,翻译为进入者或者可进入的,所以Reentrant翻译为可重复进入的.可再次进入的,因此ReentrantLock翻译为重入锁或者再入锁. 重入锁,是指一个线程获取锁之后再尝试获

  • Android实现屏幕锁定源码详解

    最近有朋友问屏幕锁定的问题,自己也在学习,网上找了下也没太详细的例子,看的资料书上也没有有关屏幕锁定程序的介绍,下个小决心,自己照着官方文档学习下,现在做好了,废话不多说,先发下截图,看下效果,需要注意的地方会加注释,有问题的朋友可以直接留言,我们共同学习交流,共同提高进步!直接看效果图: 一:未设置密码时进入系统设置的效果图如下: 二:设置密码方式预览: 三:密码解密效果图 四:九宫格解密时的效果图 下面来简单的看下源码吧,此处讲下,这个小DEMO也是临时学习下的,有讲的不明白的地方请朋友直接

  • Java并发编程之ConcurrentLinkedQueue源码详解

    一.ConcurrentLinkedQueue介绍 并编程中,一般需要用到安全的队列,如果要自己实现安全队列,可以使用2种方式: 方式1:加锁,这种实现方式就是我们常说的阻塞队列. 方式2:使用循环CAS算法实现,这种方式实现队列称之为非阻塞队列. 从点到面, 下面我们来看下非阻塞队列经典实现类:ConcurrentLinkedQueue (JDK1.8版) ConcurrentLinkedQueue 是一个基于链接节点的无界线程安全的队列.当我们添加一个元素的时候,它会添加到队列的尾部,当我们

  • python目标检测SSD算法预测部分源码详解

    目录 学习前言 什么是SSD算法 ssd_vgg_300主体的源码 学习前言 ……学习了很多有关目标检测的概念呀,咕噜咕噜,可是要怎么才能进行预测呢,我看了好久的SSD源码,将其中的预测部分提取了出来,训练部分我还没看懂 什么是SSD算法 SSD是一种非常优秀的one-stage方法,one-stage算法就是目标检测和分类是同时完成的,其主要思路是均匀地在图片的不同位置进行密集抽样,抽样时可以采用不同尺度和长宽比,然后利用CNN提取特征后直接进行分类与回归,整个过程只需要一步,所以其优势是速度

  • Android线程间通信Handler源码详解

    目录 前言 01. 用法 02.源码 03.结语 前言 在[Android]线程间通信 - Handler之使用篇主要讲了 Handler 的创建,发送消息,处理消息 三个步骤.那么接下来,我们也按照这三个步骤,从源码中去探析一下它们具体是如何实现的.本篇是关于创建源码的分析. 01. 用法 先回顾一下,在主线程和非主线程是如何创建 Handler 的. //主线程 private val mHandler: Handler = object : Handler(Looper.getMainLo

  • Spring AOP底层源码详解

    ProxyFactory的工作原理 ProxyFactory是一个代理对象生产工厂,在生成代理对象之前需要对代理工厂进行配置.ProxyFactory在生成代理对象之前需要决定到底是使用JDK动态代理还是CGLIB技术. // config就是ProxyFactory对象 // optimize为true,或proxyTargetClass为true,或用户没有给ProxyFactory对象添加interface if (config.isOptimize() || config.isProxy

  • Java8中AbstractExecutorService与FutureTask源码详解

    目录 前言 一.AbstractExecutorService 1.定义 2.submit 3.invokeAll 4.invokeAny 二.FutureTask 1.定义 2.构造方法 3.get 4.run/ runAndReset 5. cancel 三.ExecutorCompletionService 1.定义 2.submit 3.take/ poll 总结 前言 本篇博客重点讲解ThreadPoolExecutor的三个基础设施类AbstractExecutorService.F

  • python目标检测SSD算法训练部分源码详解

    目录 学习前言 讲解构架 模型训练的流程 1.设置参数 2.读取数据集 3.建立ssd网络. 4.预处理数据集 5.框的编码 6.计算loss值 7.训练模型并保存 开始训练 学习前言 ……又看了很久的SSD算法,今天讲解一下训练部分的代码.预测部分的代码可以参照https://blog.csdn.net/weixin_44791964/article/details/102496765 讲解构架 本次教程的讲解主要是对训练部分的代码进行讲解,该部分讲解主要是对训练函数的执行过程与执行思路进行详

  • Django Rest Framework实现身份认证源码详解

    目录 一.Django框架 二.身份认证的两种实现方式: 三.身份认证源码解析流程 一.Django框架 Django确实是一个很强大,用起来很爽的一个框架,在Rest Framework中已经将身份认证全都封装好了,用的时候直接导入authentication.py这个模块就好了.这个模块中5个认证类.但是我们在开发中很少用自带的认证类,而是根据项目实际需要去自己实现认证类.下面是内置的认证类 BaseAuthentication(object):所有的认证相关的类都继承自这个类,我们写的认证

随机推荐