我有如下一段 JAVA 代码,为什么 4 次都是输出 0 ?我第一反应应该是递增输出,求大佬们解答
public class DemoApp {
private static final AtomicInteger nextIndex = new AtomicInteger();
public static final int VARIABLES_TO_REMOVE_INDEX = nextVariableIndex();
public static int nextVariableIndex() {
return nextIndex.getAndIncrement();
}
public static void main(String[] args) {
System.out.println(VARIABLES_TO_REMOVE_INDEX);
System.out.println(VARIABLES_TO_REMOVE_INDEX);
System.out.println(VARIABLES_TO_REMOVE_INDEX);
System.out.println(VARIABLES_TO_REMOVE_INDEX);
}
}
上面这段代码其实摘抄自 netty InternalThreadLocalMap
public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {
private static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap =
new ThreadLocal<InternalThreadLocalMap>();
private static final AtomicInteger nextIndex = new AtomicInteger();
// Internal use only.
public static final int VARIABLES_TO_REMOVE_INDEX = nextVariableIndex();
private static final int DEFAULT_ARRAY_LIST_INITIAL_CAPACITY = 8;
public static int nextVariableIndex() {
int index = nextIndex.getAndIncrement();
if (index >= ARRAY_LIST_CAPACITY_MAX_SIZE || index < 0) {
nextIndex.set(ARRAY_LIST_CAPACITY_MAX_SIZE);
throw new IllegalStateException("too many thread-local indexed variables");
}
return index;
}
因为看到网上都说 InternalThreadLocalMap 存储元素的数组 0 号位置是一个类型为 set 的元素,所以对这段代码有点疑惑,那为什么 VARIABLES_TO_REMOVE_INDEX 不直接写 0 ?
1
geelaw 349 天前 via iPhone 1
因为直接写 0 不会导致 nextIndex 增加到 1 ?
|
2
revers 349 天前 1
nextVariableIndex() 只在赋值的时候使用了一次,所有 VARIABLES_TO_REMOVE_INDEX 值为 0 ,但是 nextIndex 值变为了 1
|
3
seedscoder OP @geelaw 我看网上大佬的源码分析文章,都是说数组 0 号位置元素的类型是 set ,看起来不需要增加 1 ;
另外我问了一下 ChatGPT ,它的回答: ``` VARIABLES_TO_REMOVE_INDEX 被定义为一个常量,其实质上是在类加载的时候通过 nextVariableIndex() 方法获取的值。在 Java 中,类加载时会执行静态代码块,这个时候 nextVariableIndex() 方法被调用,得到的结果会被赋值给 VARIABLES_TO_REMOVE_INDEX 。由于 VARIABLES_TO_REMOVE_INDEX 是一个 final 常量,它的值在类加载后就不会发生变化。 这样设计的一个优势是,在运行时获取这个值只需要一次计算,而不必每次调用的时候都重新计算。这有助于提高性能,特别是在需要频繁访问这个索引的情况下。 所以,尽管 nextVariableIndex() 方法被调用,但由于其结果在类加载时就确定了,并且是不可变的,因此被定义为一个常量。这样做的目的是为了提高效率,同时保证这个值在运行时是不可变的。 ``` 那反正只运行一次,跟直接写 0 好像没多大区别? |
4
seedscoder OP @revers 好的,感谢大佬
|
6
xuanbg 348 天前
VARIABLES_TO_REMOVE_INDEX 初始化后就没被改变过
|
7
L0L 348 天前
chatGPT:
在你给出的 Java 代码片段中,VARIABLES_TO_REMOVE_INDEX 是一个静态常量,它在类加载时被初始化。静态常量初始化是在类首次被加载到 JVM 时完成的,且该初始化过程只会执行一次。 java 复制 public static final int VARIABLES_TO_REMOVE_INDEX = nextVariableIndex(); 这行代码调用 nextVariableIndex() 方法,该方法通过 nextIndex.getAndIncrement() 获取当前的值并递增 nextIndex 。然而,VARIABLES_TO_REMOVE_INDEX 在初始化后就不会再改变,它保留了 nextVariableIndex() 方法第一次调用时的返回值,即 0 。 当你在 main 方法中多次打印 VARIABLES_TO_REMOVE_INDEX 时,你实际上每次都在打印同一个被初始化时确定的常量值,即 0 。即使 nextIndex 的值在每次调用 nextVariableIndex() 时都会递增,VARIABLES_TO_REMOVE_INDEX 仍然保持不变,因为它是一个 final 常量。 至于为什么 VARIABLES_TO_REMOVE_INDEX 不直接写成 0 ,而是通过调用方法来初始化,可能是因为 Netty 的设计者想要保持代码的一致性和可维护性。如果索引的生成逻辑需要变更,他们只需修改 nextVariableIndex() 方法的实现即可,而不需要去手动改动硬编码的值。此外,如果后续添加了新的索引,则可以确保 VARIABLES_TO_REMOVE_INDEX 的值是正确的,而不会与其他索引发生冲突。 在 InternalThreadLocalMap 的实际使用上,将 VARIABLES_TO_REMOVE_INDEX 设置成一个通过递增得到的值,可能是为了确保这个索引是唯一的,并且如果 InternalThreadLocalMap 类中还有其他静态变量也通过 nextVariableIndex() 初始化,那么每个静态变量都将得到一个不同的索引值。 最后,关于数组 0 号位置是一个类型为 set 的元素,这是 Netty 的内部实现细节,它可能是为了某种特定的优化或者内部使用的便捷性。由于 VARIABLES_TO_REMOVE_INDEX 是 public static final 的,假如它被设为了 0 ,那么它就不能代表特殊的内部状态或者其他索引了,因此可以推断 Netty 设计者有意将其设置为通过 nextVariableIndex() 方法动态生成的值。 |
8
jaffe24 348 天前
这主要涉及到类加载机制
|
9
dongisking 342 天前
```
package ClassLoader; import java.util.concurrent.atomic.AtomicInteger; public class InternalThreadLocalMap { private static final AtomicInteger nextIndex = new AtomicInteger(); public static final int VARIABLES_TO_REMOVE_INDEX = nextVariableIndex(); public static int nextVariableIndex() { System.out.println("test"); return nextIndex.getAndIncrement(); } public static void main(String[] args) { System.out.println("33"); System.out.println(VARIABLES_TO_REMOVE_INDEX); System.out.println(VARIABLES_TO_REMOVE_INDEX); System.out.println(VARIABLES_TO_REMOVE_INDEX); System.out.println(VARIABLES_TO_REMOVE_INDEX); } } ``` 输出 test 33 0 0 0 0 VARIABLES_TO_REMOVE_INDEX 在类加载的时候已经初始化了,后面没有发生过变化了 |