LinkedList源码和并发问题分析

1.LinkedList源码分析

LinkedList的是基于链表实现的java集合类,通过index插入到指定位置的时候使用LinkedList效率要比ArrayList高,以下源码分析是基于JDK1.8.

1.1 类的继承结构

LinkedList类的继承结构如如下所示:

从以上继承结构图中可以看到,最顶层接口为Iterable接口,实现此接口的类支持通过迭代器遍历集合中的元素。其他相关接口如Collection、List、Queue、Deque分别为列表,队列功能的相关接口,即此LinkedList支持列表、队列的相关操作。Serializable接口为序列化标志接口,即所有需要序列化的类都需要实现此接口。

1.2 LinkedList数据结构说明

首先我们来看下LinkedList中保存数据的内部类定义源码如下:

private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

通过以上定义可以看出来每个节点中除了保存元素的值之外还保存了前后节点的引用。即使用了链表的数据接口保存数据元素。数据结构如下图所示:

在LinkedList类中,只是定义了两个Node类型的属性,定义如下:

 transient Node<E> first;
 transient Node<E> last;

这两个属性分别表示头节点和尾节点,始终指向链表的第一个节点和最后一个节点。

1.3 关键方法源码分析

当不指定index往LinkedList中添加元素的时候默认是在链表的尾部添加数据,源代码如下

  void linkLast(E e) {
      //保存链表插入之前的最后一个节点
      final Node<E> l = last;
      //将新节点的preNode指向链表最后一个节点
      final Node<E> newNode = new Node<>(l, e, null);
      last指向插入后的尾节点
      last = newNode;
      if (l == null)
          //当第一次插入数据的时候,头节点和尾节点指向同一个节点
          first = newNode;
      else
          //插入之前的尾节点的nextNode指向新插入的节点
          l.next = newNode;
      size++;
      modCount++;
  }

插入节点的详细操作如上面代码注释。在次操作中其实last即尾节点是共享资源,当多个线程同时执行此方法的时候,其实会出现线程安全问题。具体的线程安全问题后面分析。

当指定index插入数据的时候,源代码如下所示:

public void add(int index, E element) {
       checkPositionIndex(index);

       if (index == size)
           linkLast(element);
       else
           linkBefore(element, node(index));
   }

在指定index插入元素的时候会先查询出index位置的元素,然后调用linkBefore将此元素插入到查询到的元素的前一个位置。其中linkBefore源码如下所示:

void linkBefore(E e, Node<E> succ) {
       // assert succ != null;
       final Node<E> pred = succ.prev;
       //新建节点,并将preNode指向idnex-1节点
       final Node<E> newNode = new Node<>(pred, e, succ);
       //插入前的index节点的prev指向新节点
       succ.prev = newNode;
       if (pred == null)
           first = newNode;
       else
          //index-1节点的nextNode指向新节点
           pred.next = newNode;
       size++;
       modCount++;
   }

在指定节点插入的方式如上注释所示。此操作中共享资源是插入之前index节点。同样会出现并发安全问题,下面对此问题进行分析。

2.LinkedList并发插入时节点覆盖的问题

在指定index插入或者addLast的时候都是在链表的尾部插入数据,当并发插入的时候如果出现以下执行顺序的时候,会出现插入的数据被覆盖的问题。

当多个线程同时获取到相同的尾节点的时候,然后多个线程同时在此尾节点后面插入数据的时候会出现数据覆盖的问题。

原文地址:https://www.cnblogs.com/vitasyuan/p/8955581.html

时间: 2024-08-09 08:05:59

LinkedList源码和并发问题分析的相关文章

Java集合系列之LinkedList源码分析

一.LinkedList简介 LinkedList是一种可以在任何位置进行高效地插入和移除操作的有序序列,它是基于双向链表实现的. ps:这里有一个问题,就是关于实现LinkedList的数据结构是否为循环的双向链表,上网搜了有很多文章都说是循环的,并且有的文章中但是我看了源代码觉得应该不是循环的? 例如在删除列表尾部节点的代码: private E unlinkLast(Node<E> l) { final E element = l.item; final Node<E> pr

Java中arraylist和linkedlist源码分析与性能比较

Java中arraylist和linkedlist源码分析与性能比较 1,简介 在java开发中比较常用的数据结构是arraylist和linkedlist,本文主要从源码角度分析arraylist和linkedlist的性能. 2,arraylist源码分析 Arraylist底层的数据结构是一个对象数组,有一个size的成员变量标记数组中元素的个数,如下图: * The array buffer into which the elements of the ArrayList are sto

LinkedList源码分析--jdk1.8

JDK1.8 ArrayList源码分析--jdk1.8LinkedList源码分析--jdk1.8 LinkedList概述 ??1.LinkedList是用双向链表实现的集合,基于内部类Node<E>实现的集合.??2.LinkedList支持双向链表访问.克隆.序列化,元素有序且可以重复.??3.LinkedList没有初始化大小,也没有扩容机制,通过头结点.尾节点迭代查找. LinkedList数据结构 ??数据结构是集合的精华所在,数据结构往往也限制了集合的作用和侧重点,了解各种数据

给jdk写注释系列之jdk1.6容器(2)-LinkedList源码解析

LinkedList是基于链表结构的一种List,在分析LinkedList源码前有必要对链表结构进行说明. 1.链表的概念 链表是由一系列非连续的节点组成的存储结构,简单分下类的话,链表又分为单向链表和双向链表,而单向/双向链表又可以分为循环链表和非循环链表,下面简单就这四种链表进行图解说明.           1.1.单向链表 单向链表就是通过每个结点的指针指向下一个结点从而链接起来的结构,最后一个节点的next指向null.      1. 2.单向循环链表           单向循环

【源码】LinkedList源码剖析

//----------------------------------------------------------- 转载请注明出处:http://blog.csdn.net/chdjj by Rowandjj 2014/8/8 //---------------------------------------------------------- 注:以下源码基于jdk1.7.0_11 上一篇我们分析了ArrayList,今天我们再来看下LinkedList. 首先上一幅框架图: Lin

Java集合框架之二:LinkedList源码解析

版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! LinkedList底层是通过双向循环链表来实现的,其结构如下图所示: 链表的组成元素我们称之为节点,节点由三部分组成:前一个节点的引用地址.数据.后一个节点的引用地址.LinkedList的Head节点不包含数据,每一个节点对应一个Entry对象.下面我们通过源码来分析LinkedList的实现原理. 1.Entry类源码: 1 private static class Entry<E> { 2 E element; 3 Entr

ArrayList源码和多线程安全问题分析

1.ArrayList源码和多线程安全问题分析 在分析ArrayList线程安全问题之前,我们线对此类的源码进行分析,找出可能出现线程安全问题的地方,然后代码进行验证和分析. 1.1 数据结构 ArrayList内部是使用数组保存元素的,数据定义如下: transient Object[] elementData; // non-private to simplify nested class access 在ArrayList中此数组即是共享资源,当多线程对此数据进行操作的时候如果不进行同步控

Java源码--LinkedList源码概述

与ArrayList同为List,LinkedList却展现出不同的特性.作为java.util下的另一重要容器,我们下面来探究一下LinkedList的源码实现及特性分析. 上篇文章讲述到,ArrayList用数组来存储数据,伴随数据量的变大,ArrayList动态扩充数组容量.与之不同,LinkedList使用链表来存储数据,因此它在插入/删除数据方面有着天然的优势,而在读取指定位置的元素时,性能却不及ArrayList速度快. LinkedList存储元素的变量如下: transient

ThreadLocal源码及相关问题分析

前言 在高并发的环境下,当我们使用一个公共的变量时如果不加锁会出现并发问题,例如SimpleDateFormat,但是加锁的话会影响性能,对于这种情况我们可以使用ThreadLocal.ThreadLocal是将公共变量copy一份到线程私有内存中以消除并发问题,ThreadLocal是JDK内部提供的高效解决并发问题的工具类之一,本文介绍ThreadLocal的重要方法的源码实现以及相关问题的分析. 数据结构 由上图可以看出,在Thread中维护了一个Entry的列表,Entry存储的是公共变