看 JDK 源码,解几个疑问

2022-07-15 18:49:57 浏览数 (1)

1、Java 定时器原理是怎么样的?

解包 jdk_sec-1_5_0-src-jrl,在/j2se/src/share/classes/java/util 中找到 Timer 类。

代码语言:javascript复制
private TaskQueue queue = new TaskQueue();
private TimerThread thread = new TimerThread(queue);

我找到了一个 Timer 的任务队列,找到了一个实际运行的线程类。

再来看看还有什么:

代码语言:javascript复制
    private void mainLoop() {
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
                synchronized(queue) {
                    // Wait for queue to become non-empty
                    while (queue.isEmpty() && newTasksMayBeScheduled)
                        queue.wait();
                    if (queue.isEmpty())
                        break; // Queue is empty and will forever remain; die
                    // Queue nonempty; look at first evt and do the right thing
                    long currentTime, executionTime;
                    task = queue.getMin();
                    synchronized(task.lock) {
                        if (task.state == TimerTask.CANCELLED) {
                            queue.removeMin();
                            continue;  // No action required, poll queue again
                        }
                        currentTime = System.currentTimeMillis();
                        executionTime = task.nextExecutionTime;
                        if (taskFired = (executionTime<=currentTime)) {
                            if (task.period == 0) { // Non-repeating, remove
                                queue.removeMin();
                                task.state = TimerTask.EXECUTED;
                            } else { // Repeating task, reschedule
                                queue.rescheduleMin(
                                  task.period<0 ? currentTime   - task.period
                                                : executionTime   task.period);
                            }
                        }
                    }
                    if (!taskFired) // Task hasn't yet fired; wait
                        queue.wait(executionTime - currentTime);
                }
                if (taskFired)  // Task fired; run it, holding no locks
                    task.run();
            } catch(InterruptedException e) {
            }
        }
    }

这是最核心的死循环方法,可以看见,在循环中通过不断地获取系统时间,直到特定时间到达。和我以前的理解可不一样。

2、String 类型实际是怎么实现的。

代码语言:javascript复制
/** The value is used for character storage. */
private final char value[];
/** The offset is the first index of the storage that is used. */
private final int offset;
/** The count is the number of characters in the String. */
private final int count;
/** Cache the hash code for the string */
private int hash; // Default to 0

看到了一个 char 类型数组,它才是实现 String 的根本,还有几个辅助属性。值得注意的是,String 内容实际是不可变的,举例:

代码语言:javascript复制
public String concat(String str) {
    int otherLen = str.length();
    if (otherLen == 0) {
        return this;
    }
    char buf[] = new char[count   otherLen];
    getChars(0, count, buf, 0);
    str.getChars(0, otherLen, buf, count);
    return new String(0, count   otherLen, buf);
}

这是其中的一个字符串连接的方法,可以看到 String 所有的方法,只要是牵涉到对字符串更改的,一律调用构造器生成一个新的返回,而根本不更改本身的内容,不过 StringBuffer 的内容却是可变的,看源码便知。

3、关于 Thread 类 sleep 方法参数里的纳秒。

我们都知道 Thread 实现了 Runnable 接口。不过现在我们看看里面的一个有趣的方法:

代码语言:javascript复制
public static void sleep(long millis, int nanos) 
    throws InterruptedException {
    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException("nanosecond timeout value out of range");
    }
    if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
        millis  ;
    }
    sleep(millis);
}

看到了吧,纳秒——根本就是假的,我们都被 JDK 骗了。Java 常规控制线程的时间精度是非常低的,根本不可能接近纳秒的级别,至于你传入的纳秒参数,下场就是要么变成 0,要么变成 1 毫秒。

4、容器类的容量变化的实现。

以 Vector 为例吧,找到了一个需要变化容量的方法:

代码语言:javascript复制
//这是它实际存储对象的数组
protected Object[] elementData;
private void ensureCapacityHelper(int minCapacity) {
    int oldCapacity = elementData.length;
    if (minCapacity > oldCapacity) {
        Object[] oldData = elementData;
        int newCapacity = (capacityIncrement > 0) ?
            (oldCapacity   capacityIncrement) : (oldCapacity * 2);
        if (newCapacity < minCapacity) {
            newCapacity = minCapacity;
        }
        elementData = new Object[newCapacity];
        System.arraycopy(oldData, 0, elementData, 0, elementCount);
    }
}

可以看到它的容器大小增长策略,如果有合理的增量,当然听用户的,否则简单地乘 2 完事。

这只是随便挑了几个感兴趣的 JDK 里的类看一看而已,相信进一步的研究会有更多收获。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

×Scan to share with WeChat

0 人点赞