看JDK源码,解疑惑
从SUN公司主页上搞下来JDK5.0的源码,可以好好研究了。开始吧。
1、Java定时器原理是怎么样的?
解包jdk_sec-1_5_0-src-jrl,在/j2se/src/share/classes/java/util中找到Timer类。
- private TaskQueue queue = new TaskQueue();
- private TimerThread thread = new TimerThread(queue);
我们找到了一个Timer的任务队列,找到了一个实际运行的线程类。
再来看看还有什么:
- 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类型实际是怎么实现的。
- /** 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内容实际是不可变的,举例:
- 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。
我们都知道Thread实现了Runnable接口。不过现在我们看看里面的一个有趣的方法:
- 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为例吧,找到了一个需要变化容量的方法:
- //这是它实际存储对象的数组
- 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的类看一看而已,相信进一步的研究会有更多收获。