关于一段 Java 代码的疑问,求解答

145 天前
 seedscoder

我有如下一段 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 ?

1664 次点击
所在节点    Java
9 条回复
geelaw
145 天前
因为直接写 0 不会导致 nextIndex 增加到 1 ?
revers
145 天前
nextVariableIndex() 只在赋值的时候使用了一次,所有 VARIABLES_TO_REMOVE_INDEX 值为 0 ,但是 nextIndex 值变为了 1
seedscoder
145 天前
@geelaw 我看网上大佬的源码分析文章,都是说数组 0 号位置元素的类型是 set ,看起来不需要增加 1 ;

另外我问了一下 ChatGPT ,它的回答:


```

VARIABLES_TO_REMOVE_INDEX 被定义为一个常量,其实质上是在类加载的时候通过 nextVariableIndex() 方法获取的值。在 Java 中,类加载时会执行静态代码块,这个时候 nextVariableIndex() 方法被调用,得到的结果会被赋值给 VARIABLES_TO_REMOVE_INDEX 。由于 VARIABLES_TO_REMOVE_INDEX 是一个 final 常量,它的值在类加载后就不会发生变化。

这样设计的一个优势是,在运行时获取这个值只需要一次计算,而不必每次调用的时候都重新计算。这有助于提高性能,特别是在需要频繁访问这个索引的情况下。

所以,尽管 nextVariableIndex() 方法被调用,但由于其结果在类加载时就确定了,并且是不可变的,因此被定义为一个常量。这样做的目的是为了提高效率,同时保证这个值在运行时是不可变的。
```



那反正只运行一次,跟直接写 0 好像没多大区别?
seedscoder
145 天前
@revers 好的,感谢大佬
yumenawei
145 天前
@livid 很好奇三楼这种情况你怎么处理?
xuanbg
145 天前
VARIABLES_TO_REMOVE_INDEX 初始化后就没被改变过
L0L
145 天前
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() 方法动态生成的值。
jaffe24
144 天前
这主要涉及到类加载机制
dongisking
139 天前
```
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 在类加载的时候已经初始化了,后面没有发生过变化了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1000833

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX