我的原则:先会用再说,内部慢慢来。
学以致用,根据场景学源码
一、架构
1.1 代码架构图
1.2 状态流程图
1.2.1 简易流程图
1.2.2 UML流程图 (单ReferenceQueue)
1.2.3 UML流程图 (多ReferenceQueue)
1.2.4 Reference对象的状态通过成员变量next和queue来判断
状态 | Next | Queue | discovered |
---|---|---|---|
Active | null | referenceQueue | 下一个要回收的 reference(gc决定)(假如是最后一个,那么就是 this ) |
Pending | this | referenceQueue | 下一个要进入队列的 reference (假如是最后一个,那么就是 this ) |
Enqueued | 队列中下一个节点(假如是最后一个,next = this) | ReferenceQueue.ENQUEUED | null |
Inactive | this | ReferenceQueue.NULL | null |
在 Active 与 Pending 状态下的, discovered 就相当于 next。
1.2.5 未注册 ReferenceQueue
如果未注册 Queue,那么就只有 Active 与 Inactive 状态。
二、ReferenceQueue 源码剖析
ReferenceQueue队列是一个单向链表,ReferenceQueue里面只有一个head 成员变量持有队列的队头。后进先出的队列,其实是个就是个栈!!!
2.1 ReferenceQueue 类
ReferenceQueue和 Reference 类都是 jdk1.2 的时候出的,所以也就不可能继承jdk1.5出来的Queue接口
package java.lang.ref;
import java.util.function.Consumer;
public class ReferenceQueue<T> {
public ReferenceQueue() {
}
private static class Null<S> extends ReferenceQueue<S> {
boolean enqueue(Reference<? extends S> r) {
return false; //内部类,它是用来做状态识别的,重写了enqueue入队方法,永远返回false,所以它不会存储任何数据,见后面的NULL和ENQUEUED两个标识成员变量
}
}
/*
状态State:Inactive
1. 当 Reference对象创建时没有指定 queue,设置为 NULL
2. 该 Reference 被拉出队列的时候,设置为 NULL
*/
static ReferenceQueue<Object> NULL = new Null<>();
/*
状态State:ENQUEUED
Reference已经被ReferenceHander线程从pending队列移到queue里面时。设置为 ENQUEUED
所以ENQUEUED状态不可能在调用 enqueue 方法
*/
static ReferenceQueue<Object> ENQUEUED = new Null<>();
static private class Lock {
};
// 锁对象,只是用来这个类加锁用
private Lock lock = new Lock();
// 头节点(有存放数据)
private volatile Reference<? extends T> head = null;
// 队列真实长度
private long queueLength = 0;
// 如果Reference创建时没有指定队列或Reference对象已经在队列里面了,则直接返回
boolean enqueue(Reference<? extends T> r) {
}
private Reference<? extends T> reallyPoll() {
}
public Reference<? extends T> poll() {
if (head == null)
return null;
synchronized (lock) {
return reallyPoll();
}
}
//将头部第一个对象移出队列并返回,如果队列为空,则等待timeout时间后,返回null,这个方法会阻塞线程
public Reference<? extends T> remove(long timeout){
}
//将队列头部第一个对象从队列中移除出来,如果队列为空则直接返回null(此方法不会被阻塞)
public Reference<? extends T> remove(){
return remove(0);}
void forEach(Consumer<? super Reference<? extends T>> action) {
}
}
2.1.1 Null 内部类
- java.lang.ref.ReferenceQueue.Null
- 内部类,它是用来做状态识别的,重写了enqueue入队方法,永远返回false,所以它不会存储任何数据,见后面的NULL和ENQUEUED两个标识成员变量
private static class Null<S> extends ReferenceQueue<S> {
boolean enqueue(Reference<? extends S> r) {
return false;
}
}
2.2 enqueue 方法
boolean enqueue(Reference<? extends T> r) {
synchronized (lock) {
// Check that since getting the lock this reference hasn't already been
// enqueued (and even then removed)
ReferenceQueue<?> queue = r.queue;
// 如果Reference创建时没有指定队列或Reference对象已经在队列里面了,则直接返回
if ((queue == NULL) || (queue == ENQUEUED)) {
return false;
}
assert queue == this; //!!! 只有r的队列是当前队列才允许入队
r.queue = ENQUEUED; //将r的queue设置为ENQUEUED状态,标识Reference已经入队
r.next = (head == null) ? r : head; // 往头部插 ,类似于栈结构</