我最近得到了一次NPE,当时我绝对确定这是不可能的.这个类是在一个非常多线程的程序中使用的,所以我知道您应该认为基本上任何事情都是可能的,但仍然.

因此,该类定义了以下字段:

private final Set<TcpIpConnection> _connections = new LinkedHashSet<>();

在整个班级中,这个集合只在两个地方被操纵:

// some method
  TcpIpConnection tcpipConnection = new ServerConnection(clientSocket, _channels, MyClass.this);
  _connections.add(tcpipConnection);
// some other method
  _connections.remove(connection);

所以我想你会同意,null是不可能加到这一组的.是的,布景留在班级里,永远不会扩散到外面.

但现在我有一个测试用例,它有时会失败,并在以下语句中使用NPE,这是类中唯一使用_connections的另一条语句:

new ArrayList<>(_connections).stream().forEach(c -> c.close("Server down"));

正如您所看到的,我已经通过首先创建集合的本地副本AS ArrayList来防止ConcurrentModificationException.

现在NPE显示为c,这一定是之前添加到_connections上的值-但它怎么会变成null呢?

需要明确的是,我并不是在寻找解决方案--我在流中添加了filter(Objects::nonNull)(或者我在初始化器中使用了Collections.synchronizedSet()),现在它肯定可以工作了.

这怎么可能发生呢?是的,多线程访问几乎会把一切都搞砸,但把null放在一个不存在的集合中呢?

推荐答案

只是为了解释为什么可以为空

以下是参考JDK-21+35

In new ArrayList<>(_connections), which will call
_connections.toArray()
-> HashSet#toArray()(as LinkedHashSet extends HashSet)
-> LinkedHashMap#keysToArray()(as LinkedHashSet is backed by LinkedHashMap)

    // HashSet#toArray()
    @Override
    public Object[] toArray() {
        return map.keysToArray(new Object[map.size()]);
    }

正如我们在这里看到的,构造了一个长度为map.size()的对象[],该对象[]将被复制到ArrayList.

    // LinkedHashMap#keysToArray()
    final <T> T[] keysToArray(T[] a) {
        return keysToArray(a, false);
    }

    final <T> T[] keysToArray(T[] a, boolean reversed) {
        Object[] r = a;
        int idx = 0;
        if (reversed) {
            for (LinkedHashMap.Entry<K,V> e = tail; e != null; e = e.before) {
                r[idx++] = e.key;
            }
        } else {
            for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
                r[idx++] = e.key;
            }
        }
        return a;
    }

上面是我们如何为对象[]设置值. 因此,在并发环境中,当我们调用HashSet#toArray()时,映射大小可能是2,但当我们转到LinkedHashMap#keysToArray()时,映射大小可能是1(中间的某个线程移除了元素),因此对象[]中可能有一些空元素.反之亦然,ArrayIndexOutOfBound可能被抛出.

结论

Never assume any behaviour of non thread safe class when working in multi-thread environment, there is 0 guarantee.

Java相关问答推荐

@从类文件中删除JsonProperty—Java

我想了解Java中的模块化.编译我的应用程序时,我有一个ResolutionException

Java Stream,需要更新列表对象列表

在spring—data中自动发现native—sql查询期间遇到重复的SQL别名[id]

弹簧靴和龙目岛

有没有一种方法使保持活动设置专用于java.net.http.HttpClient的一个实例

基本时态运算的ISO-8601周数据表示法

FALSE:它应该在什么时候使用?

为什么不应用类型推断?

由于我在Main方法中关闭了 scanner ,但在该方法中创建了一个新的 scanner ,因此出现了错误

当返回Mono<;Something>;时,不会调用Mono<;void>;.flatMap

使用存储在字符串变量中的路径目录打开.pdf文件

如何将Pane的图像快照保存为BMP?

如何通过Java java.lang.Foreign API访问本机字节数组

Maven-Dependency-Plugin 3.6.+开始查找在依赖关系:分析目标期间找到的新的使用的未声明依赖关系

获取所有可以处理Invent.ACTION_MEDIA_BUTTON Android 13 API33的Android包

如何在Spring Security中设置一个任何人都可以打开的主页?

java.lang.NoSuchMethodError:';org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream$Builder org.apache.poi-poi-ooxml-5.2.4

由于可为null,无法在kotlin中实现java接口

spring 数据Elastic search 与 spring 启动数据Elastic search 之间的区别是什么?