zoukankan      html  css  js  c++  java
  • 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的理解。

  • 相关阅读:
    01.网页学习阶段、整站分析、规划
    书签搬运
    如何判断两个链表相交及找到第一个相交点
    Windows平台使用git bash管理github中的工程
    二级指针的操作
    结构体的内存对齐
    大端和小端
    剑指Offer——面试题26:复杂链表的复制
    使用editcap命令将ERF格式转换为pcap格式
    如何在STL的map中使用结构体作为键值
  • 原文地址:https://www.cnblogs.com/yanzs/p/13788278.html
Copyright © 2011-2022 走看看