diff --git a/README.md b/README.md index 1b5a971e..ea31a328 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,6 @@ 专注分享后端技术干货,包括 Java 基础、Java 并发、JVM、Nginx、Zookeeper、微服务、消息队列、源码解析、数据库、设计模式、面经等,助你编程之路少走弯路。 -有一句话说得很好,一个人学习可以走得很快,但一群人学习可以走得更远。 - -所以,如果你想和众多优秀的人一起学习,可以考虑加入技术交流群。扫描下方微信二维码,备注【加群】添加好友,我会迅速拉你进群。 - -![微信二维码](https://img-blog.csdnimg.cn/202001101531450.jpg) - ## 基础 String 源码 @@ -34,10 +28,16 @@ Runnable 源码解析 +ThreadPoolExecutor 源码解析 + ## 其他 Object 源码解析 Runtime 源码解析 -ThreadLocal 源码 \ No newline at end of file +ThreadLocal 源码解析 + +InheritableThreadLocal 源码解析 + +WeakReference 源码解析 diff --git a/src/java/lang/InheritableThreadLocal.java b/src/java/lang/InheritableThreadLocal.java index 9d75035f..c12b8ff1 100644 --- a/src/java/lang/InheritableThreadLocal.java +++ b/src/java/lang/InheritableThreadLocal.java @@ -1,59 +1,13 @@ -/* - * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved. - * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ - package java.lang; -import java.lang.ref.*; /** - * This class extends ThreadLocal to provide inheritance of values - * from parent thread to child thread: when a child thread is created, the - * child receives initial values for all inheritable thread-local variables - * for which the parent has values. Normally the child's values will be - * identical to the parent's; however, the child's value can be made an - * arbitrary function of the parent's by overriding the childValue - * method in this class. - * - *

Inheritable thread-local variables are used in preference to - * ordinary thread-local variables when the per-thread-attribute being - * maintained in the variable (e.g., User ID, Transaction ID) must be - * automatically transmitted to any child threads that are created. + * 创建允许子线程继承的 ThreadLocal * - * @author Josh Bloch and Doug Lea - * @see ThreadLocal - * @since 1.2 + * @see ThreadLocal */ - public class InheritableThreadLocal extends ThreadLocal { /** - * Computes the child's initial value for this inheritable thread-local - * variable as a function of the parent's value at the time the child - * thread is created. This method is called from within the parent - * thread before the child is started. - *

- * This method merely returns its input argument, and should be overridden - * if a different behavior is desired. + * 拿到父线程的值后,可以在这里处理后再返回给子线程 * * @param parentValue the parent thread's value * @return the child thread's initial value @@ -63,18 +17,18 @@ protected T childValue(T parentValue) { } /** - * Get the map associated with a ThreadLocal. + * 获取当前线程内的 inheritableThreadLocals 属性 * - * @param t the current thread + * @param t 当前线程 */ ThreadLocalMap getMap(Thread t) { - return t.inheritableThreadLocals; + return t.inheritableThreadLocals; } /** - * Create the map associated with a ThreadLocal. + * 初始化线程中的 inheritableThreadLocals 属性 * - * @param t the current thread + * @param t 当前线程 * @param firstValue value for the initial entry of the table. */ void createMap(Thread t, T firstValue) { diff --git a/src/java/lang/Integer.java b/src/java/lang/Integer.java index 1a3165f9..63f5069c 100644 --- a/src/java/lang/Integer.java +++ b/src/java/lang/Integer.java @@ -1,28 +1,3 @@ -/* - * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. - * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ - package java.lang; import java.lang.annotation.Native; @@ -801,8 +776,9 @@ private static class IntegerCache { cache = new Integer[(high - low) + 1]; int j = low; - for(int k = 0; k < cache.length; k++) + for(int k = 0; k < cache.length; k++) { cache[k] = new Integer(j++); + } // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; @@ -827,8 +803,9 @@ private IntegerCache() {} * @since 1.5 */ public static Integer valueOf(int i) { - if (i >= IntegerCache.low && i <= IntegerCache.high) + if (i >= IntegerCache.low && i <= IntegerCache.high) { return IntegerCache.cache[i + (-IntegerCache.low)]; + } return new Integer(i); } diff --git a/src/java/lang/String.java b/src/java/lang/String.java index fabb7b74..4fdcfd04 100644 --- a/src/java/lang/String.java +++ b/src/java/lang/String.java @@ -82,9 +82,7 @@ * @see java.nio.charset.Charset * @since JDK1.0 */ - -public final class String - implements java.io.Serializable, Comparable, CharSequence { +public final class String implements java.io.Serializable, Comparable, CharSequence { /** * String底层是使用字符数组存储的 */ diff --git a/src/java/lang/ThreadLocal.java b/src/java/lang/ThreadLocal.java index 43044fb3..72610902 100644 --- a/src/java/lang/ThreadLocal.java +++ b/src/java/lang/ThreadLocal.java @@ -1,102 +1,63 @@ package java.lang; + import java.lang.ref.*; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; /** - * This class provides thread-local variables. These variables differ from - * their normal counterparts in that each thread that accesses one (via its - * {@code get} or {@code set} method) has its own, independently initialized - * copy of the variable. {@code ThreadLocal} instances are typically private - * static fields in classes that wish to associate state with a thread (e.g., - * a user ID or Transaction ID). - * - *

For example, the class below generates unique identifiers local to each - * thread. - * A thread's id is assigned the first time it invokes {@code ThreadId.get()} - * and remains unchanged on subsequent calls. - *

- * import java.util.concurrent.atomic.AtomicInteger;
+ * 线程局部缓存:为线程缓存数据,数据独享
+ * 原理:
+ * 1. 每个线程由一个 ThreadLocalMap 属性,本质就是一个 map
+ * 2. map 里面存储的  称为键值对,存储键值对时需要先求取哈希值
+ * 3. map 里存储的 key 是一个弱引用,其包装了当前线程中构造的 ThreadLocal 对象
+ * 这意味着,只要 ThreadLocal 对象丢掉了强引用,那么在下次 GC 后,map 中的 ThreadLocal 对象也会被清除
+ * 对于那些 ThreadLocal 对象为空的 map 元素,会当为垃圾,稍后会被主动清理
+ * 4. map 里存储的 value 就是缓存到当前线程的值,这个 value 没有弱引用去包装,需要专门的释放策略
+ * 5. 一个线程对应多个 ThreadLocal,一个 ThreadLocal 只对应一个值
  *
- * public class ThreadId {
- *     // Atomic integer containing the next thread ID to be assigned
- *     private static final AtomicInteger nextId = new AtomicInteger(0);
+ * 哈希值碰撞的问题:
+ * 如果是单线程,因为魔数 HASH_INCREMENT 的存在,且不断扩容,这里不容易出现碰撞
+ * 但如果是多线程,哈希值就很容易出现碰撞,因为属性 nextHashCode 是各线程共享的,会导致生成的哈希值出现重复
  *
- *     // Thread local variable containing each thread's ID
- *     private static final ThreadLocal<Integer> threadId =
- *         new ThreadLocal<Integer>() {
- *             @Override protected Integer initialValue() {
- *                 return nextId.getAndIncrement();
- *         }
- *     };
+ * ThreadLocal value = new ThreadLocal<>();
+ *  形成 map 的键值对,value 作为 ThreadLocalMap 中的键,用它来查找匹配的值。
  *
- *     // Returns the current thread's unique ID, assigning it if necessary
- *     public static int get() {
- *         return threadId.get();
- *     }
- * }
- * 
- *

Each thread holds an implicit reference to its copy of a thread-local - * variable as long as the thread is alive and the {@code ThreadLocal} - * instance is accessible; after a thread goes away, all of its copies of - * thread-local instances are subject to garbage collection (unless other - * references to these copies exist). - * - * @author Josh Bloch and Doug Lea - * @since 1.2 + * @param */ public class ThreadLocal { + /** - * ThreadLocals rely on per-thread linear-probe hash maps attached - * to each thread (Thread.threadLocals and - * inheritableThreadLocals). The ThreadLocal objects act as keys, - * searched via threadLocalHashCode. This is a custom hash code - * (useful only within ThreadLocalMaps) that eliminates collisions - * in the common case where consecutively constructed ThreadLocals - * are used by the same threads, while remaining well-behaved in - * less common cases. + * 当前 ThreadLocal 的 hashCode,由 nextHashCode() 计算而来,用于计算当前 ThreadLocal 在 ThreadLocalMap 中的索引位置 + * 一个线程可以有多个 ThreadLocal 实例,各实例之内的原始 hashCode 不相同 + * 一个 ThreadLocal 实例也可被多个线程共享,此时多个线程内看到的原始 hashCode 是相同的 */ private final int threadLocalHashCode = nextHashCode(); /** - * The next hash code to be given out. Updated atomically. Starts at - * zero. + * static + AtomicInteger 保证了在一台机器中每个 ThreadLocal 的 threadLocalHashCode 是唯一的 + * 被 static 修饰非常关键,因为一个线程在处理业务的过程中,ThreadLocalMap 是会被 set 多个 ThreadLocal 的,多个 ThreadLocal 就依靠 threadLocalHashCode 进行区分 + * 所有 ThreadLocal 共享,但每次构造一个 ThreadLocal 实例,其值都会更新 */ private static AtomicInteger nextHashCode = new AtomicInteger(); /** - * The difference between successively generated hash codes - turns - * implicit sequential thread-local IDs into near-optimally spread - * multiplicative hash values for power-of-two-sized tables. + * HASH_INCREMENT 是一个特殊哈希魔数,这主要与斐波那契散列法以及黄金分割有关 */ private static final int HASH_INCREMENT = 0x61c88647; /** - * Returns the next hash code. + * 返回计算出的下一个哈希值,其值为 i * HASH_INCREMENT,其中 i 代表调用次数 */ private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); } /** - * Returns the current thread's "initial value" for this - * thread-local variable. This method will be invoked the first - * time a thread accesses the variable with the {@link #get} - * method, unless the thread previously invoked the {@link #set} - * method, in which case the {@code initialValue} method will not - * be invoked for the thread. Normally, this method is invoked at - * most once per thread, but it may be invoked again in case of - * subsequent invocations of {@link #remove} followed by {@link #get}. + * 为 ThreadLocal 对象设置关联的初值,具体逻辑可由子类实现 * - *

This implementation simply returns {@code null}; if the - * programmer desires thread-local variables to have an initial - * value other than {@code null}, {@code ThreadLocal} must be - * subclassed, and this method overridden. Typically, an - * anonymous inner class will be used. - * - * @return the initial value for this thread-local + * @return 该线程关联的初始值 */ protected T initialValue() { return null; @@ -117,103 +78,103 @@ public static ThreadLocal withInitial(Supplier supplier) { } /** - * Creates a thread local variable. + * 构造 ThreadLocal 实例 + * * @see #withInitial(java.util.function.Supplier) */ public ThreadLocal() { } /** - * Returns the value in the current thread's copy of this - * thread-local variable. If the variable has no value for the - * current thread, it is first initialized to the value returned - * by an invocation of the {@link #initialValue} method. + * 返回当前 ThreadLocal 对象关联的值 * - * @return the current thread's value of this thread-local + * @return */ public T get() { + // 返回当前 ThreadLocal 所在的线程 Thread t = Thread.currentThread(); + // 从线程中拿到 ThreadLocalMap ThreadLocalMap map = getMap(t); if (map != null) { + // 从 map 中拿到 entry ThreadLocalMap.Entry e = map.getEntry(this); + // 如果不为空,读取当前 ThreadLocal 中保存的值 if (e != null) { @SuppressWarnings("unchecked") - T result = (T)e.value; + T result = (T) e.value; return result; } } + // 若 map 为空,则对当前线程的 ThreadLocal 进行初始化,最后返回当前的 ThreadLocal 对象关联的初值,即 value return setInitialValue(); } /** - * Variant of set() to establish initialValue. Used instead - * of set() in case user has overridden the set() method. + * 初始化 ThreadLocalMap,并存储键值对 ,最后返回 value * - * @return the initial value + * @return value */ private T setInitialValue() { + // 获取为 ThreadLocal 对象设置关联的初值 T value = initialValue(); Thread t = Thread.currentThread(); + // 返回当前线程 t 持有的 map ThreadLocalMap map = getMap(t); - if (map != null) + if (map != null) { map.set(this, value); - else + } else { + // 为当前线程初始化 map,并存储键值对 createMap(t, value); + } return value; } /** - * Sets the current thread's copy of this thread-local variable - * to the specified value. Most subclasses will have no need to - * override this method, relying solely on the {@link #initialValue} - * method to set the values of thread-locals. + * 为当前 ThreadLocal 对象关联 value 值 * - * @param value the value to be stored in the current thread's copy of - * this thread-local. + * @param value 要存储在此线程的线程副本的值 */ public void set(T value) { + // 返回当前 ThreadLocal 所在的线程 Thread t = Thread.currentThread(); + // 返回当前线程持有的map ThreadLocalMap map = getMap(t); - if (map != null) + if (map != null) { + // 如果 ThreadLocalMap 不为空,则直接存储键值对 map.set(this, value); - else + } else { + // 否则,需要为当前线程初始化 ThreadLocalMap,并存储键值对 createMap(t, value); + } } /** - * Removes the current thread's value for this thread-local - * variable. If this thread-local variable is subsequently - * {@linkplain #get read} by the current thread, its value will be - * reinitialized by invoking its {@link #initialValue} method, - * unless its value is {@linkplain #set set} by the current thread - * in the interim. This may result in multiple invocations of the - * {@code initialValue} method in the current thread. - * - * @since 1.5 + * 清理当前 ThreadLocal 对象关联的键值对 */ public void remove() { + // 返回当前线程持有的 map ThreadLocalMap m = getMap(Thread.currentThread()); - if (m != null) + if (m != null) { + // 从 map 中清理当前 ThreadLocal 对象关联的键值对 m.remove(this); + } } /** - * Get the map associated with a ThreadLocal. Overridden in - * InheritableThreadLocal. + * 返回当前线程 thread 持有的 ThreadLocalMap * - * @param t the current thread - * @return the map + * @param t 当前线程 + * @return ThreadLocalMap */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; } /** - * Create the map associated with a ThreadLocal. Overridden in - * InheritableThreadLocal. + * 为当前线程初始化map,并存储键值对 * - * @param t the current thread - * @param firstValue value for the initial entry of the map + * @param t 当前线程 + * @param firstValue 要设置的 value 值 */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); @@ -261,29 +222,25 @@ protected T initialValue() { } /** - * ThreadLocalMap is a customized hash map suitable only for - * maintaining thread local values. No operations are exported - * outside of the ThreadLocal class. The class is package private to - * allow declaration of fields in class Thread. To help deal with - * very large and long-lived usages, the hash table entries use - * WeakReferences for keys. However, since reference queues are not - * used, stale entries are guaranteed to be removed only when - * the table starts running out of space. + * 类似HashMap,进行元素存取时,要清理遇到的垃圾值,且合并原先紧密相邻的元素(除去垃圾值会造成新空槽) */ static class ThreadLocalMap { /** - * The entries in this hash map extend WeakReference, using - * its main ref field as the key (which is always a - * ThreadLocal object). Note that null keys (i.e. entry.get() - * == null) mean that the key is no longer referenced, so the - * entry can be expunged from table. Such entries are referred to - * as "stale entries" in the code that follows. + * 键值对实体的存储结构 */ static class Entry extends WeakReference> { - /** The value associated with this ThreadLocal. */ + /** + * 当前线程关联的 value,这个 value 并没有用弱引用追踪 + */ Object value; + /** + * 构造键值对 + * + * @param k k 作 key,作为 key 的 ThreadLocal 会被包装为一个弱引用 + * @param v v 作 value + */ Entry(ThreadLocal k, Object v) { super(k); value = v; @@ -291,54 +248,55 @@ static class Entry extends WeakReference> { } /** - * The initial capacity -- MUST be a power of two. + * 初始容量,必须为 2 的幂 */ private static final int INITIAL_CAPACITY = 16; /** - * The table, resized as necessary. - * table.length MUST always be a power of two. + * 存储 ThreadLocal 的键值对实体数组,长度必须为 2 的幂 */ private Entry[] table; /** - * The number of entries in the table. + * ThreadLocalMap 元素数量 */ private int size = 0; /** - * The next size value at which to resize. + * 扩容的阈值,默认是数组大小的三分之二 */ - private int threshold; // Default to 0 + private int threshold; /** - * Set the resize threshold to maintain at worst a 2/3 load factor. + * 设置扩容阙值 */ private void setThreshold(int len) { threshold = len * 2 / 3; } /** - * Increment i modulo len. + * 哈希值发生冲突时,计算下一个哈希值,此处使用线性探测寻址,只是简单地将索引加 1 */ private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); } /** - * Decrement i modulo len. + * 线性探测,向前遍历 */ private static int prevIndex(int i, int len) { return ((i - 1 >= 0) ? i - 1 : len - 1); } /** - * Construct a new map initially containing (firstKey, firstValue). - * ThreadLocalMaps are constructed lazily, so we only create - * one when we have at least one entry to put in it. + * 初始化 ThreadLocalMap,并存储键值对 + * + * @param firstKey + * @param firstValue */ ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; + // i 是一个 [0, INITIAL_CAPACITY) 之间的值 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; @@ -376,27 +334,25 @@ private ThreadLocalMap(ThreadLocalMap parentMap) { } /** - * Get the entry associated with key. This method - * itself handles only the fast path: a direct hit of existing - * key. It otherwise relays to getEntryAfterMiss. This is - * designed to maximize performance for direct hits, in part - * by making this method readily inlinable. + * 返回 key 关联的键值对实体 * - * @param key the thread local object - * @return the entry associated with key, or null if no such + * @param key threadLocal + * @return */ private Entry getEntry(ThreadLocal key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; - if (e != null && e.get() == key) + // 若 e 不为空,并且 e 的 ThreadLocal 的内存地址和 key 相同,直接返回 + if (e != null && e.get() == key) { return e; - else + } else { + // 从 i 开始向后遍历找到键值对实体 return getEntryAfterMiss(key, i, e); + } } /** - * Version of getEntry method for use when key is not found in - * its direct hash slot. + * 从 i 开始向后遍历找到键值对实体 * * @param key the thread local object * @param i the table index for key's hash code @@ -409,68 +365,75 @@ private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) { while (e != null) { ThreadLocal k = e.get(); - if (k == key) + if (k == key) { return e; - if (k == null) + } + // 遇到了垃圾值 + if (k == null) { + // 从索引 i 开始,遍历一段连续的元素,清理其中的垃圾值,并使各元素排序更紧凑 expungeStaleEntry(i); - else + } else { i = nextIndex(i, len); + } e = tab[i]; } return null; } /** - * Set the value associated with key. + * 在 map 中存储键值对 * - * @param key the thread local object - * @param value the value to be set + * @param key threadLocal + * @param value 要设置的 value 值 */ private void set(ThreadLocal key, Object value) { - - // We don't use a fast path as with get() because it is at - // least as common to use set() to create new entries as - // it is to replace existing ones, in which case, a fast - // path would fail more often than not. - Entry[] tab = table; int len = tab.length; - int i = key.threadLocalHashCode & (len-1); - - for (Entry e = tab[i]; - e != null; - e = tab[i = nextIndex(i, len)]) { + // 计算 key 在数组中的下标 + int i = key.threadLocalHashCode & (len - 1); + // 遍历一段连续的元素,以查找匹配的 ThreadLocal 对象 + for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { + // 获取该哈希值处的ThreadLocal对象 ThreadLocal k = e.get(); + // 键值ThreadLocal匹配,直接更改map中的value if (k == key) { e.value = value; return; } + // 若 key 是 null,说明 ThreadLocal 被清理了,直接替换掉 if (k == null) { replaceStaleEntry(key, value, i); return; } } + // 直到遇见了空槽也没找到匹配的ThreadLocal对象,那么在此空槽处安排ThreadLocal对象和缓存的value tab[i] = new Entry(key, value); int sz = ++size; - if (!cleanSomeSlots(i, sz) && sz >= threshold) + // 如果没有元素被清理,那么就要检查当前元素数量是否超过了容量阙值(数组大小的三分之二),以便决定是否扩容 + if (!cleanSomeSlots(i, sz) && sz >= threshold) { + // 扩容的过程也是对所有的 key 重新哈希的过程 rehash(); + } } /** - * Remove the entry for key. + * 从 map 中清理 key 关联的键值对 + * + * @param key */ private void remove(ThreadLocal key) { Entry[] tab = table; int len = tab.length; - int i = key.threadLocalHashCode & (len-1); + int i = key.threadLocalHashCode & (len - 1); for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { if (e.get() == key) { e.clear(); + // 从索引 i 开始,遍历一段连续的元素,清理其中的垃圾值,并使各元素排序更紧凑 expungeStaleEntry(i); return; } @@ -551,32 +514,28 @@ private void replaceStaleEntry(ThreadLocal key, Object value, } /** - * Expunge a stale entry by rehashing any possibly colliding entries - * lying between staleSlot and the next null slot. This also expunges - * any other stale entries encountered before the trailing null. See - * Knuth, Section 6.4 + * 从索引staleSlot开始,遍历一段连续的元素,清理其中的垃圾值,并使各元素排序更紧凑 * - * @param staleSlot index of slot known to have null key - * @return the index of the next null slot after staleSlot - * (all between staleSlot and this slot will have been checked - * for expunging). + * @param staleSlot + * @return 终止遍历过程的空槽下标 */ private int expungeStaleEntry(int staleSlot) { Entry[] tab = table; int len = tab.length; - // expunge entry at staleSlot + // 索引 staleSlot 处本身标识的就是一个垃圾值,所以需要首先清理掉 tab[staleSlot].value = null; tab[staleSlot] = null; size--; - // Rehash until we encounter null Entry e; int i; + // 继续往后遍历连续的Entry数组,直到遇见一个空槽后停止遍历 for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal k = e.get(); + // 如果当前Entry已经不包含 ThreadLocal,说明这是个垃圾值,需要清理 if (k == null) { e.value = null; tab[i] = null; @@ -651,31 +610,37 @@ private void rehash() { } /** - * Double the capacity of the table. + * 扩容,重新计算索引,标记垃圾值,方便 GC 回收 */ private void resize() { Entry[] oldTab = table; int oldLen = oldTab.length; int newLen = oldLen * 2; + // 新建一个数组,按照2倍长度扩容 Entry[] newTab = new Entry[newLen]; int count = 0; + // 将旧数组的值拷贝到新数组上 for (int j = 0; j < oldLen; ++j) { Entry e = oldTab[j]; if (e != null) { ThreadLocal k = e.get(); + // 若有垃圾值,则标记清理该元素的引用,以便GC回收 if (k == null) { - e.value = null; // Help the GC + e.value = null; } else { + // 计算 ThreadLocal 在新数组中的位置 int h = k.threadLocalHashCode & (newLen - 1); - while (newTab[h] != null) + // 如果发生冲突,使用线性探测往后寻找合适的位置 + while (newTab[h] != null) { h = nextIndex(h, newLen); + } newTab[h] = e; count++; } } } - + // 设置新的扩容阈值,为数组长度的三分之二 setThreshold(newLen); size = count; table = newTab; diff --git a/src/java/lang/management/MemoryUsage.java b/src/java/lang/management/MemoryUsage.java index 4bd56756..f002a31a 100644 --- a/src/java/lang/management/MemoryUsage.java +++ b/src/java/lang/management/MemoryUsage.java @@ -1,28 +1,3 @@ -/* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. - * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ - package java.lang.management; import javax.management.openmbean.CompositeData; @@ -104,9 +79,21 @@ * @since 1.5 */ public class MemoryUsage { + /** + * JVM 在启动期间从操作系统请求的用于内存管理的初始内存容量(以字节为单位) + */ private final long init; + /** + * 当前已经使用的内存量(以字节为单位) + */ private final long used; + /** + * 保证可以由 JVM 使用的内存量(以字节为单位) + */ private final long committed; + /** + * 用于内存管理的最大内存量(以字节为单位) + */ private final long max; /** @@ -236,6 +223,7 @@ public long getMax() { /** * Returns a descriptive representation of this memory usage. */ + @Override public String toString() { StringBuffer buf = new StringBuffer(); buf.append("init = " + init + "(" + (init >> 10) + "K) "); diff --git a/src/java/lang/ref/WeakReference.java b/src/java/lang/ref/WeakReference.java index 707dba58..f9fd6688 100644 --- a/src/java/lang/ref/WeakReference.java +++ b/src/java/lang/ref/WeakReference.java @@ -1,69 +1,29 @@ -/* - * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved. - * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ - package java.lang.ref; - /** - * Weak reference objects, which do not prevent their referents from being - * made finalizable, finalized, and then reclaimed. Weak references are most - * often used to implement canonicalizing mappings. - * - *

Suppose that the garbage collector determines at a certain point in time - * that an object is weakly - * reachable. At that time it will atomically clear all weak references to - * that object and all weak references to any other weakly-reachable objects - * from which that object is reachable through a chain of strong and soft - * references. At the same time it will declare all of the formerly - * weakly-reachable objects to be finalizable. At the same time or at some - * later time it will enqueue those newly-cleared weak references that are - * registered with reference queues. + * 弱引用(Weak Reference) + * 当一个对象为弱引用时,运行 GC 后会回收其引用指向的对象 + * 它可以用于解决非静态内部类的内存泄露问题(定义一个静态内部类,并让它持有外部类的弱引用) + * 还可以用于实现缓存,比如 WeakHashMap * - * @author Mark Reinhold - * @since 1.2 + * @param */ - public class WeakReference extends Reference { /** - * Creates a new weak reference that refers to the given object. The new - * reference is not registered with any queue. + * 对 referent 对象进行弱引用 * - * @param referent object the new weak reference will refer to + * @param referent 被弱引用的对象 */ public WeakReference(T referent) { super(referent); } /** - * Creates a new weak reference that refers to the given object and is - * registered with the given queue. + * 对 referent 对象进行弱引用,在对象被回收后,会把弱引用对象,也就是 WeakReference 对象或者其子类的对象,放入队列 ReferenceQueue 中 * - * @param referent object the new weak reference will refer to - * @param q the queue with which the reference is to be registered, - * or null if registration is not required + * @param referent 被弱引用的对象 + * @param q 引用队列 */ public WeakReference(T referent, ReferenceQueue q) { super(referent, q); diff --git a/src/java/util/ArrayList.java b/src/java/util/ArrayList.java index ed9fa285..a3c96265 100644 --- a/src/java/util/ArrayList.java +++ b/src/java/util/ArrayList.java @@ -71,7 +71,7 @@ public class ArrayList extends AbstractList /** * 带有容量initialCapacity的构造方法 * - * @param 初始容量列表的初始容量 + * @param initialCapacity 初始容量列表的初始容量 * @throws IllegalArgumentException 如果指定容量为负 */ public ArrayList(int initialCapacity) { @@ -84,8 +84,7 @@ public ArrayList(int initialCapacity) { this.elementData = EMPTY_ELEMENTDATA; } else {// 小于0 // 则抛出IllegalArgumentException异常 - throw new IllegalArgumentException("Illegal Capacity: " + - initialCapacity); + throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity); } } @@ -107,8 +106,9 @@ public ArrayList(Collection c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toarray可能(错误地)不返回对象[](见JAVA BUG编号6260652) - if (elementData.getClass() != Object[].class) + if (elementData.getClass() != Object[].class) { elementData = Arrays.copyOf(elementData, size, Object[].class); + } } else { // 使用空数组 this.elementData = EMPTY_ELEMENTDATA; diff --git a/src/java/util/concurrent/ConcurrentHashMap.java b/src/java/util/concurrent/ConcurrentHashMap.java index dcb90d0b..0b6047ba 100644 --- a/src/java/util/concurrent/ConcurrentHashMap.java +++ b/src/java/util/concurrent/ConcurrentHashMap.java @@ -46,7 +46,7 @@ public class ConcurrentHashMap extends AbstractMap private static final float LOAD_FACTOR = 0.75f; /** - * 链表转树的阀值,如果table[i]下面的链表长度大于8时就转化为数 + * 链表转树的阀值,如果table[i]下面的链表长度大于8时就转化为树 */ static final int TREEIFY_THRESHOLD = 8; diff --git a/src/java/util/concurrent/Executors.java b/src/java/util/concurrent/Executors.java index 422631f7..88bf8491 100644 --- a/src/java/util/concurrent/Executors.java +++ b/src/java/util/concurrent/Executors.java @@ -1,38 +1,3 @@ -/* - * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ - -/* - * - * - * - * - * - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - package java.util.concurrent; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; @@ -591,7 +556,7 @@ public T run() throws Exception { } /** - * The default thread factory + * 默认的线程工厂 */ static class DefaultThreadFactory implements ThreadFactory { private static final AtomicInteger poolNumber = new AtomicInteger(1); diff --git a/src/java/util/concurrent/ThreadPoolExecutor.java b/src/java/util/concurrent/ThreadPoolExecutor.java index d1f59291..e2fba877 100644 --- a/src/java/util/concurrent/ThreadPoolExecutor.java +++ b/src/java/util/concurrent/ThreadPoolExecutor.java @@ -1,38 +1,3 @@ -/* - * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ - -/* - * - * - * - * - * - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - package java.util.concurrent; import java.security.AccessControlContext; @@ -905,11 +870,9 @@ private boolean addWorker(Runnable firstTask, boolean core) { int rs = runStateOf(c); // Check if queue empty only if necessary. - if (rs >= SHUTDOWN && - ! (rs == SHUTDOWN && - firstTask == null && - ! workQueue.isEmpty())) + if (rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty())) { return false; + } for (;;) { int wc = workerCountOf(c); @@ -953,6 +916,7 @@ private boolean addWorker(Runnable firstTask, boolean core) { } finally { mainLock.unlock(); } + // 如果成功添加了 Worker,就可以启动 Worker 了 if (workerAdded) { t.start(); workerStarted = true; @@ -1069,9 +1033,7 @@ private Runnable getTask() { } try { - Runnable r = timed ? - workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : - workQueue.take(); + Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; timedOut = true; @@ -1133,15 +1095,9 @@ final void runWorker(Worker w) { try { while (task != null || (task = getTask()) != null) { w.lock(); - // If pool is stopping, ensure thread is interrupted; - // if not, ensure thread is not interrupted. This - // requires a recheck in second case to deal with - // shutdownNow race while clearing interrupt - if ((runStateAtLeast(ctl.get(), STOP) || - (Thread.interrupted() && - runStateAtLeast(ctl.get(), STOP))) && - !wt.isInterrupted()) + if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) { wt.interrupt(); + } try { beforeExecute(wt, task); Throwable thrown = null; @@ -1340,43 +1296,48 @@ public ThreadPoolExecutor(int corePoolSize, * @throws NullPointerException if {@code command} is null */ public void execute(Runnable command) { - if (command == null) + // 若任务为空,则抛 NPE,不能执行空任务 + if (command == null) { throw new NullPointerException(); - /* - * Proceed in 3 steps: - * - * 1. If fewer than corePoolSize threads are running, try to - * start a new thread with the given command as its first - * task. The call to addWorker atomically checks runState and - * workerCount, and so prevents false alarms that would add - * threads when it shouldn't, by returning false. - * - * 2. If a task can be successfully queued, then we still need - * to double-check whether we should have added a thread - * (because existing ones died since last checking) or that - * the pool shut down since entry into this method. So we - * recheck state and if necessary roll back the enqueuing if - * stopped, or start a new thread if there are none. - * - * 3. If we cannot queue task, then we try to add a new - * thread. If it fails, we know we are shut down or saturated - * and so reject the task. - */ + } int c = ctl.get(); + // 若工作线程数小于核心线程数,则创建新的线程,并把当前任务 command 作为这个线程的第一个任务 if (workerCountOf(c) < corePoolSize) { - if (addWorker(command, true)) + if (addWorker(command, true)) { return; + } c = ctl.get(); } + /** + * 至此,有以下两种情况: + * 1.当前工作线程数大于等于核心线程数 + * 2.新建线程失败 + * 此时会尝试将任务添加到阻塞队列 workQueue + */ + // 若线程池处于 RUNNING 状态,将任务添加到阻塞队列 workQueue 中 if (isRunning(c) && workQueue.offer(command)) { + // 再次检查线程池标记 int recheck = ctl.get(); - if (! isRunning(recheck) && remove(command)) + // 如果线程池已不处于 RUNNING 状态,那么移除已入队的任务,并且执行拒绝策略 + if (!isRunning(recheck) && remove(command)) { + // 任务添加到阻塞队列失败,执行拒绝策略 reject(command); - else if (workerCountOf(recheck) == 0) + } + // 如果线程池还是 RUNNING 的,并且线程数为 0,那么开启新的线程 + else if (workerCountOf(recheck) == 0) { addWorker(null, false); + } } - else if (!addWorker(command, false)) + /** + * 至此,有以下两种情况: + * 1.线程池处于非运行状态,线程池不再接受新的线程 + * 2.线程处于运行状态,但是阻塞队列已满,无法加入到阻塞队列 + * 此时会尝试以最大线程数为限制创建新的工作线程 + */ + else if (!addWorker(command, false)) { + // 任务进入线程池失败,执行拒绝策略 reject(command); + } } /** diff --git a/src/java/util/concurrent/atomic/AtomicInteger.java b/src/java/util/concurrent/atomic/AtomicInteger.java index 514f0f72..e1c68153 100644 --- a/src/java/util/concurrent/atomic/AtomicInteger.java +++ b/src/java/util/concurrent/atomic/AtomicInteger.java @@ -1,38 +1,3 @@ -/* - * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ - -/* - * - * - * - * - * - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - package java.util.concurrent.atomic; import java.util.function.IntUnaryOperator; import java.util.function.IntBinaryOperator; diff --git a/test/java/lang/IntegerTest.java b/test/java/lang/IntegerTest.java new file mode 100644 index 00000000..eb426c91 --- /dev/null +++ b/test/java/lang/IntegerTest.java @@ -0,0 +1,19 @@ +package java.lang; + +/** + * IntegerCache 会缓存[-128,127]的 Integer(为了自动装箱的复用),valueOf 方法会从缓存中去拿值,如果命中缓存,会减少资源的开销 + * 对象池模式:空间换时间 + * + * @author wupx + * @date 2020/04/26 + */ +public class IntegerTest { + public static void main(String[] args) { + Integer i1 = 66;// 通过反编译可以知道 Integer i = 66; 这种形式声明的变量是通过 java.lang.Integer#valueOf(int) 来构造 Integer 对象的 + Integer i2 = 66; + Integer i3 = 128; + Integer i4 = 128; + System.out.println(i1 == i2);// true,在[-128,127]内时,会直接从 IntegerCache 中获取 Integer + System.out.println(i3 == i4);// false,属于两个对象 + } +} \ No newline at end of file diff --git a/test/java/lang/IntegerTest2.java b/test/java/lang/IntegerTest2.java new file mode 100644 index 00000000..89cdcb6f --- /dev/null +++ b/test/java/lang/IntegerTest2.java @@ -0,0 +1,19 @@ +package java.lang; + +/** + * 当一个 Integer 和 int 比较时,Integer 会调用 intValue() 方法自动拆箱后再比较 + * + * @author wupx + * @date 2020/04/27 + */ +public class IntegerTest2 { + public static void main(String[] args) { + Integer i1 = new Integer(5); + Integer i2 = new Integer(5);// 通过 new 来创建的两个 Integer 对象 + Integer i3 = 5;// 调用 valueOf 将 5 自动装箱成 Integer 类型 + int i4 = 5;// 基本数据类型 5 + System.out.println(i1 == i3); // false,两个引用没有引用同一对象 + System.out.println(i1 == i2); // false,两个通过 new 创建的 Integer 对象不是同一个引用 + System.out.println(i3 == i4); // true,i3 调用 java/lang/Integer.intValue:()I 自动拆箱成 int 类型再和 i4 比较 + } +} \ No newline at end of file diff --git a/test/java/lang/IntegerTest3.java b/test/java/lang/IntegerTest3.java new file mode 100644 index 00000000..f3eaac0a --- /dev/null +++ b/test/java/lang/IntegerTest3.java @@ -0,0 +1,15 @@ +package java.lang; + +/** + * getInteger 方法从系统属性中获取值,然后再装箱,如果系统属性中没有该值,则返回备用值 val + * + * @author wupx + * @date 2020/04/27 + */ +public class IntegerTest3 { + public static void main(String[] args) { + System.setProperty("age", "18"); + Integer age = Integer.getInteger("age", 25); + System.out.println(age); + } +} \ No newline at end of file diff --git a/test/java/lang/MemoryUsageTest.java b/test/java/lang/MemoryUsageTest.java new file mode 100644 index 00000000..94e90100 --- /dev/null +++ b/test/java/lang/MemoryUsageTest.java @@ -0,0 +1,25 @@ +package java.lang; + +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryManagerMXBean; +import java.lang.management.MemoryUsage; +import java.util.List; + +/** + * @author wupx + * @date 2020/04/28 + */ +public class MemoryUsageTest { + + public static void main(String[] args) { + MemoryMXBean memoryMxBean = ManagementFactory.getMemoryMXBean(); + MemoryUsage memoryUsage = memoryMxBean.getHeapMemoryUsage(); + System.out.println(memoryUsage); + List memoryManagerMXBeans = ManagementFactory.getMemoryManagerMXBeans(); + for (MemoryManagerMXBean memoryManagerMXBean : memoryManagerMXBeans) { + System.out.println(memoryManagerMXBean.getName()); + } + } + +} diff --git a/test/java/lang/StringTest.java b/test/java/lang/StringTest.java new file mode 100644 index 00000000..21e527b1 --- /dev/null +++ b/test/java/lang/StringTest.java @@ -0,0 +1,21 @@ +package java.lang; + +/** + * @author wupx + * @date 2020/04/27 + */ +public class StringTest { + public static void main(String[] args) { +// String name = "wupx"; +// String substring = name.substring(0, 1); +// System.out.println(name); +// System.out.println(substring); + variableParameter("1","2"); + } + + public static void variableParameter(String... strings){ + for (String string : strings) { + System.out.println(string); + } + } +}