先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
正文
Condition 实现的语义为 Object.wait 与 Object.notify。
关于Condition 的实现类为 AbstractQueuedSynchronizer.ConditionObject 内部类。
首先在讲解源码之前,我重点罗列出ConditionObject的关键数据结构:
private transient Node fristWaiter;
private transient Node lastWaiter;
从这里看出,每个CondtionObject,都维护着自己的条件等待等待队列,并且是一个双端链表。
1.1 void await() throws InterruptedException
/**
-
Implements interruptible condition wait.
-
- If current thread is interrupted, throw InterruptedException.
- Save lock state returned by {@link #getState}.
- Invoke {@link #release} with
-
saved state as argument, throwing
-
IllegalMonitorStateException if it fails.
- Block until signalled or interrupted.
- Reacquire by invoking specialized version of
-
{@link #acquire} with saved state as argument.
- If interrupted while blocked in step 4, throw InterruptedException.
*/
public final void await() throws InterruptedException {
if (Thread.interrupted()) // @1
throw new InterruptedException();
Node node = addConditionWaiter(); //@2
int savedState = fullyRelease(node); // @3
int interruptMode = 0;
while (!isOnSyncQueue(node)) { //@4
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) // @5
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE) //@6
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled //@7
unlinkCancelledWaiters();
if (interruptMode != 0) //@8
reportInterruptAfterWait(interruptMode);
}
代码@1:检测当前线程的中断标记,如果中断位为1,则抛出异常。
代码@2:添加等待节点。就是一个简单的链表维护节点的操作,具体参照addConditionWaiter讲解。
代码@3:释放占有的锁,并获取当前锁的state,因为await实现的语意为Object.wait,释放锁并并等待条件的发生。当条件满足后,线程被唤醒后,第一步是需要获取锁,然后在上次await的下一条指令处继续执行。代码3就是实现上述语义的释放锁。
代码@4:isOnSyncQueue 当前节点是否在同步队列中,如果在同步阻塞队列中,则申请锁,去执行;如果不在同步队列中(在条件队列中),阻塞,等待满足条件,新增的节点,默认在条件队列中(Conditon)。isOnSyncQueue 源码解读在下文中;
代码@5:线程从条件等待被唤醒,唤醒后,线程要从条件队列移除,进入到同步等待队列,被唤醒有有如下两种情况,一是条件满足,收到singal信号,二是线程被取消(中断),该步骤是从条件队列移除,加入到同步等待队列,返回被唤醒的原因,如果是被中断,需要根据不同模式,处理中断。处理中断,也有两种方式:1.继续设置中断位;2:直接抛出InterruptedException。请看下文关于checkInterruptWhileWaiting的源码解读。
代码@6:运行到代码6时,说明线程已经结束了释放锁,从条件队列移除,线程运行,在继续执行业务逻辑之前,必须先获取锁。只有成功获取锁后,才会去判断线程的中断标志,才能在中断标志为真时,抛出InterruptException。
代码@7,执行一些收尾工作,清理整个条件队列:
代码@8,处理中断,是设置中断位,还是抛出InterruptException。
那我们先关注一下addConditionWaiter方法:
/**
-
Adds a new waiter to wait queue.
-
@return its new wait node
*/
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) { //@1
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION); //@2
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
添加条件等待节点,根据链表的特征,直接在尾部节点的nextWaiter指向新建的节点,并将新建的节点设置为整个链表的尾部,首先要知道如下数据结构:
object {
Node firstWaiter;
Node lastWaiter;
node {
node nextWaiter;
该节点承载的业务数据,比如这里的Thread t;等
}
}
知道上述结构,其实整个链的数据维护,基本一目了然,自己都可以实现下面的逻辑。
代码@1,如果最后一个等待节点的状态不是Node.CONDITION,则,则先删除等待链中节点状态不为Node.CONDITION的节点。具体代码分析请参照下文unlinkCancelledWaiters的解读。
代码@2开始,就是普通链表的节点添加的基本方法。
清除等待节点方法。
/**
-
Unlinks cancelled waiter nodes from condition queue.
-
Called only while holding lock. This is called when
-
cancellation occurred during condition wait, and upon
-
insertion of a new waiter when lastWaiter is seen to have
-
been cancelled. This method is needed to avoid garbage
-
retention in the absence of signals. So even though it may
-
require a full traversal, it comes into play only when
-
timeouts or cancellations occur in the absence of
-
signals. It traverses all nodes rather than stopping at a
-
particular target to unlink all pointers to garbage nodes
-
without requiring many re-traversals during cancellation
-
storms.
*/
private void unlinkCancelledWaiters() {
Node t = firstWaiter; //
Node trail = null; //@1
while (t != null) {
Node next = t.nextWaiter;
if (t.waitStatus != Node.CONDITION) { // @3
t.nextWaiter = null;
if (trail == null) // @4
firstWaiter = next;
else
trail.nextWaiter = next; //@5
if (next == null) // @6
lastWaiter = trail;
}
else // @4
trail = t;
t = next;
}
}
该方法的思路为,从第一节点开始,将不等于Node.CONDITION的节点。
代码@1,设置尾部节点临时变量,用来记录最终的尾部节点。代码@1 第一次循环,是循环第一个节点,如果它的状态为Node.CONDITION, 则该链的头节点保持不变,设置临时尾节点为t,然后进行一个节点的判断,如果节点不为Node.CONDITION, 重置头节点的下一个节点,或尾部节点的下一个节点(@4,@5)。代码@6代表整个循环结束,设置 ConditionObject对象的lastWaiter为trail的值;
await步骤中,释放锁过程源码解析。释放锁的过程,逻辑为unlock,但该方法,返回当前锁的state,因为释放锁后,该方法在条件没有满足前提下,自身需要阻塞。被唤醒后,需要先尝试获取锁,然后才能执行接下来的逻辑。
/**
-
Invokes release with current state value; returns saved state.
-
Cancels node and throws exception on failure.
-
@param node the condition node for this wait
-
@return previous sync state
*/
final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
await,@4步骤中,isOnSyncQueue 源码解读:
/**
-
Returns true if a node, always one that was initially placed on
-
a condition queue, is now waiting to reacquire on sync queue.
-
@param node the node
-
@return true if is reacquiring
*/
final boolean isOnSyncQueue(Node node) {
if (node.waitStatus == Node.CONDITION || node.prev == null) // @1
return false;
if (node.next != null) // If has successor, it must be on queue // @2
return true;
/*
-
node.prev can be non-null, but not yet on queue because
-
the CAS to place it on queue can fail. So we have to
-
traverse from tail to make sure it actually made it. It
-
will always be near the tail in calls to this method, and
-
unless the CAS failed (which is unlikely), it will be
-
there, so we hardly ever traverse much.
*/
return findNodeFromTail(node);
}
代码@1,如果节点的状态为Node.CONDITION 或 node.prev == null,表明该节点在条件队列中,并没有加入同步阻塞队列(同步阻塞队列为申请锁等待的队列),await方法中,新增的节点,默认满足上述条件,所以返回false,表示在条件队列中,等待条件的发生,条件满足之前,当前线程应该阻塞。这里,先预留一个疑问,那node.prev在什么时候会改变呢?
代码@2,如果node.next不为空,说明在同步阻塞队列中。这个我想毫无疑问。当然也说明next域肯定是在进入同步队列过程中会设置值。
代码@3, 上面的注释也说的比较清楚,node.prev不为空,但也不在同步队列中,这个是由于CAS可能会失败,为了不丢失信号,从同步队列中再次选择该节点,如果找到则返回true,否则返回false,在这里,我就更加对node.prev在什么时候会设置值感兴趣了,请继续await方法向下看,总有水落石出的时候。
await @5 checkInterruptWhileWaiting 代码解读:
/*
-
For interruptible waits, we need to track whether to throw
-
InterruptedException, if interrupted while blocked on
-
condition, versus reinterrupt current thread, if
-
interrupted while blocked waiting to re-acquire.
*/
/** Mode meaning to reinterrupt on exit from wait */
private static final int REINTERRUPT = 1; // 重新设置中断位,中断由上层处理
/** Mode meaning to throw InterruptedException on exit from wait */
private static final int THROW_IE = -1; // 直接抛出 InterruptedException 0:正常
/**
-
Checks for interrupt, returning THROW_IE if interrupted
-
before signalled, REINTERRUPT if after signalled, or
-
0 if not interrupted.
*/
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
terrupted
-
before signalled, REINTERRUPT if after signalled, or
-
0 if not interrupted.
*/
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-wCCuJHXX-1713624206937)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!