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的理解。

  • 相关阅读:
    mysql 函数 存储过程 事件(event) job 模板
    protobuf 无proto 解码 decode 语言 java python
    mitmproxy fiddler 抓包 填坑
    android adb 常用命令
    android机器人 模拟 踩坑过程
    RabbitMQ添加新用户并支持远程访问
    Windows下RabbitMQ安装及配置
    Java mybatis mysql 常用数据类型对应关系
    easyExcel 踩坑
    linux防火墙查看状态firewall、iptable
  • 原文地址:https://www.cnblogs.com/yanzs/p/13788278.html
Copyright © 2011-2022 走看看