并发场景下list的空指针异常和size大小问题

Posted 啥也不会,啥也没有!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并发场景下list的空指针异常和size大小问题相关的知识,希望对你有一定的参考价值。

并发场景下list的空指针异常和size大小问题

问题描述:

对一个源list使用并行流对其进行遍历的时候往宿list添加元素,再次遍历宿list的时候会抛出空指针异常问题而且会现宿list size大小也有问题。

问题复原:

@org.junit.Test
public void test2() {
    List<Integer> source = new LinkedList<>();
    List<String> target = new LinkedList<>();

    for (int i = 0; i < 1000; i++) {
        source.add(i);
    }

    source.parallelStream().forEach(i -> {
        target.add(String.valueOf(i));
    });

    System.out.println("target size -> " + target.size());

    for (String s : target) {
        System.out.println(s);
    }

}

运行结果

问题分析:

不开启多线程的时候targetList的size一定是1000的,而且不会出现空指针异常。

这两个问题其实都和size++这句话有关

1. size大小为什么不是1000

分析源码:

add是尾插,源码如下

void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

模拟一个场景

  1. size现在的值为100
  2. 线程a拿到了size大小100,此时cpu让出执行权给线程b
  3. 线程b拿到了size大小也为100
  4. 那么无论他们谁先加1,最后size的值会被101覆盖两次,导致size的大小不会是102

2. 再次遍历target的时候为什么会报空指针异常问题

其实也和尾插法这段代码有关

模拟场景:

  1. 设现在的size为100

  2. 线程a拿到了链表尾部元素last 之后让出执行权给线程b

  3. 线程b也拿到了相同的last之后一直执行完了add操作,此时size = 101

  4. 线程a也执行了 l.next = newNode;覆盖了线程b的在size = 101 位置上的值,之后也进行了size++的操作,size = 102

  5. 此时出现的问题就是size = 101 位置上的值被覆盖了两次,但是102位置上的值是null。

  6. 所以遍历的时候会报空指针异常。

  7. 因为last = newNode;这一步的重复覆盖,可以预测所有的null都会是链表末尾。

    3. 关于为什么foreach增强for循环为什么会报空指针的问题

    Linkedlist的foreach循环,是依赖Iterator的,LinkedList也有自己的迭代器,源码如下

    可以看出hasnext的判断并不是node.next != null 而是 nextIndex < size 所以即使null都在末尾也会在 next = next.next的时候报空指针异常。

    3. 问题解决

    List<String> target = Collections.synchronizedList(new LinkedList<>());
    

    让需要进行add操作的list,转换成线程安全的。

    关于ArrayList

    其实也是和size++有关,与LinkedList不同的是由于实现尾插的方式不一致,所以导致null可能在中间

    源码如下:

    结果如下:

标度适应性测试的空指针异常

[创建一个scaleFitnessTest算法来测试binarry值的适合度并使用权重(质数),然后将它们在标尺的'左侧'和'右侧'之间移动

实际规模适应性的逻辑有效,但是在测试null参数时,我得到了nullpointer异常

我试图满足的要求是:

该方法在以下情况下应返回-100:

1]如果“ rep”或“ weights”的大小为零,或者二者之一为零空。

2)如果“ rep”的大小大于权数的数量。

我的代码如下

public static double ScalesFitness(ArrayList<Boolean> rep, ArrayList<Double> weights)   
        if( (rep.size() == 0 || weights.size() == 0) || (rep == null || weights == null) ) 
            return (-100);
        
        if (rep.size() > weights.size()) 
            return(-100);
        
        double lhs = 0.0,rhs = 0.0;
        int n = rep.size();
        for(int i=0;i<n;i++)
            if (rep.get(i) == true) 
                lhs = lhs + weights.get(i);

            else 
                rhs = rhs + weights.get(i);

            
        


        return(Math.abs(lhs-rhs));
    
答案
如@jsheeran和@ D.Lawrence所述,在对它们调用size()之前先检查是否为空

以上是关于并发场景下list的空指针异常和size大小问题的主要内容,如果未能解决你的问题,请参考以下文章

服务器中的空指针异常

记录一些遇见的bug——记录一个使用多线程异步调用openfeign时子线程丢失request请求头导致的空指针异常错误

java中的空指针异常怎么解决

自定义页面适配器上的空指针异常

Grails/Hibernate:版本控制上的空指针异常

NULL空指针常见情况(修复和定位)