Java源码阅读------ReferenceQueue

描述

引用队列,在Reference类中辅助Reference的实现,对于注册ReferenceQueue的Reference对象,gc检测到可达性更改后,会将其加入到其中。

实现过程

构造方法

比较空洞的构造方法。。。。

public ReferenceQueue() { }

内部类

Null

用于辅助表示ReferenceQueue对应Reference的状况,通过继承ReferenceQueue实现,覆盖原有的enqueue入队方法,使其始终返回false。

private static class Null<S> extends ReferenceQueue<S> {
	boolean enqueue(Reference<? extends S> r) {
    	return false;
    }
}

对应在ReferenceQueue中有两个静态变量NULL,ENQUEUED。

static ReferenceQueue<Object> NULL = new Null<>();
static ReferenceQueue<Object> ENQUEUED = new Null<>();

NULL表示没有注册ReferenceQueue的状态,ENQUEUED表示对应的Reference已经入队了。之后的逻辑操作中会以此来区分。

Lock

同Reference中的使用一样,使用一个空的Object类来实现同步。

static private class Lock { };
private Lock lock = new Lock();

队列

在Reference得分析中我们看到了next引用变量,这一变量用于描述结点指向的下一个元素,除此之外,ReferenceQueue中还有一个head变量来指明队列的首部,queueLength变量指明队列长度。

Reference next;//Reference类
private volatile Reference<? extends T> head = null;//在ReferenceQueue中默认为null
private long queueLength = 0;//默认长度为0

入队

入队操作是由enqueue函数实现的。传入的是一个Reference,而且该函数必须由对应注册ReferenceQueue的Reference才能调用。

boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
	synchronized (lock) {
    	// Check that since getting the lock this reference hasn't already been
    	// enqueued (and even then removed)
    	ReferenceQueue<?> queue = r.queue;
        if ((queue == NULL) || (queue == ENQUEUED)) {
        	return false;
        }
        assert queue == this;
        r.queue = ENQUEUED;
        r.next = (head == null) ? r : head;
        head = r;
        queueLength++;
        if (r instanceof FinalReference) {
        	sun.misc.VM.addFinalRefCount(1);
        }
        lock.notifyAll();
        return true;
    }
}

首先,通过传入的Reference获取对应的ReferenceQueue,存储在queue即自身this。通过判断queue的参数是否标记为NULL(没有注册ReferenceQueue),ENQUEUED(注册了但是对应的Reference已经在队里了),排除这些后将其返回false,入队操作失败。剩下的使用断言语句保证Reference中的ReferenceQueue与自身类的对应关系。这些是准备工作。
入队过程开始,先将queue标记为ENQUEUED防止重复入队。接着判断之前是否含有入队元素,如果有就将其放到传入的Reference的后面(next),并重新设置传入的Reference为队头,如果之前的队为空队列则将传入的Reference的next指向自身。最后如果传入的Reference是FinalReference的话,就使用addFinalRefCount使计数加一,便于jvm处理。一切完成后通知等待入队的进程并返回入队成功。

出队

出队操作是由reallyPoll函数实现的。

@SuppressWarnings("unchecked")
private Reference<? extends T> reallyPoll() {       /* Must hold lock */
	Reference<? extends T> r = head;
    if (r != null) {
    	head = (r.next == r) ? null : r.next; // Unchecked due to the next field having a raw type in Reference
        r.queue = NULL;
        r.next = r;
        queueLength--;
        if (r instanceof FinalReference) {
        	sun.misc.VM.addFinalRefCount(-1);
        }
        return r;
	}
    return null;
}

也很简单的操作,先判断出队的对象不为空,也就是队首,判断出队后队中是否还有其他的元素,如果没有(出队的Reference中next指向自己)就将head置空,如果有就将head指向出队的Reference的next,之后清除出队Reference的引用队列,将其next指向自身,数量减一,判断出队的Reference是否为FinalReference,如果是就将对应的计数减一。返回出队的Reference。将出队方法封装:

public Reference<? extends T> poll() {
	if (head == null)
		return null;
    synchronized (lock) {
    	return reallyPoll();
    }
}

移除

这一方法从队列中移除一个Reference。当队列为空时就等待一段时间直到移除一个Reference或者超时。timeout为超时时间。

public Reference<? extends T> remove(long timeout) throws IllegalArgumentException, InterruptedException {
	if (timeout < 0) {
    	throw new IllegalArgumentException("Negative timeout value");
    }
    synchronized (lock) {
        Reference<? extends T> r = reallyPoll();
        if (r != null) return r;
        long start = (timeout == 0) ? 0 : System.nanoTime();
        for (;;) {
        	lock.wait(timeout);
            r = reallyPoll();
            if (r != null) return r;
            if (timeout != 0) {
            	long end = System.nanoTime();
                timeout -= (end - start) / 1000_000;
                if (timeout <= 0) return null;
                start = end;
            }
        }
    }
}

首先进行出队操作,如果出队不为空则完成移除,若队空则根据超时时间来判断设定起始时间(纳秒),之后循环进行,交出锁并等待,之后进行出队,判断使是否出队成功,根据超时时间判断是否超时,如果超时就返回null。
除此之外还有一个默认超时为0的函数,超时设为0即没有超时限制,直到从队列中获取一个移除的Reference对象。

public Reference<? extends T> remove() throws InterruptedException {
	return remove(0);
}

小结

ReferenceQueue的实现很简单,就是一个队列的维护和相关的同步操作,但是其在Reference的使用中十分重要。充分理解ReferenceQueue有利于我们对Reference的理解。

猜你喜欢

转载自blog.csdn.net/sinat_36945592/article/details/87106118