LinkedList source code and concurrency problem analysis

1. LinkedList source code analysis

LinkedList is a java collection class based on linked list. When inserting into the specified position through index, the efficiency of using LinkedList is higher than that of ArrayList. The following source code analysis is based on JDK1.8.

1.1 Inheritance structure of classes

The inheritance structure of the LinkedList class is as follows:

As can be seen from the above inheritance structure diagram, the top-level interface is the Iterable interface, and the class implementing this interface supports traversing the elements in the collection through iterators. Other related interfaces such as Collection, List, Queue, and Deque are lists and related interfaces of queue functions, that is, this LinkedList supports operations related to lists and queues. The Serializable interface is a serialization flag interface, that is, all classes that need to be serialized need to implement this interface.

1.2 LinkedList data structure description

First, let's take a look at the source code of the internal class definition that saves data in LinkedList as follows:

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;
        }
    }

From the above definition, it can be seen that in addition to saving the value of the element, each node also saves the reference of the previous and previous nodes. That is, the data interface of the linked list is used to save the data elements. The data structure is shown in the following figure:

In the LinkedList class, only two properties of type Node are defined, which are defined as follows:

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

These two properties represent the head node and the tail node, respectively, and always point to the first and last nodes of the linked list.

1.3 Source code analysis of key methods

When adding elements to the LinkedList without specifying an index, the default is to add data at the end of the linked list. The source code is as follows

  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++;
  }

The detailed operation of inserting a node is as commented in the above code. In this operation, the last node is actually a shared resource. When multiple threads execute this method at the same time, thread safety problems will occur. The specific thread safety issues will be analyzed later.

When specifying index to insert data, the source code is as follows:

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

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

When specifying an index to insert an element, the element at the index position will be queried first, and then linkBefore will be called to insert the element into the previous position of the queried element. The linkBefore source code is as follows:

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++;
   }

The way to insert at the specified node is shown in the comment above. The shared resource in this operation is inserted before the index node. There will also be concurrency security problems, which will be analyzed below.

2. The problem of node coverage when LinkedList is inserted concurrently

When specifying index insertion or addLast, data is inserted at the end of the linked list. When concurrent insertion occurs in the following execution order, the inserted data will be overwritten.

When multiple threads acquire the same tail node at the same time, and then multiple threads insert data behind the tail node at the same time, there will be a data coverage problem.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324983544&siteId=291194637