This article directory :

  • What are the common collections?
  • The difference between List, Set and Map
  • ArrayList understand?
  • The expansion mechanism of ArrayList?
  • How do I remove an element while iterating through an ArrayList?
  • Difference between Arraylist and Vector
  • Difference between Arraylist and LinkedList
  • HashMap

    • What are the ways to resolve hash conflicts? Which HashMap is used?
    • The hash algorithm used?
    • Expansion process?
    • put method process?
    • What are the characteristics of a red-black tree?
    • Why use a red-black tree instead of an AVL tree?
    • When resolving hash conflicts, why choose to use linked list first, and then switch to red-black tree?
    • Why is the length of HashMap a power of 2?
    • What is the default load factor for HashMap? Why 0.75?
    • What is generally used as the key of HashMap?
    • Why is HashMap not thread safe?
    • Difference between HashMap and HashTable?
  • The underlying principle of LinkedHashMap?
  • Talk about TreeMap?
  • The underlying principle of HashSet?
  • Difference between HashSet, LinkedHashSet and TreeSet?
  • What is fail fast?
  • What is failsafe?
  • Talk about ArrayDeque?
  • Which collection classes are thread safe? Which are unsafe?
  • What is Iterator?
  • What is the difference between Iterator and ListIterator?
  • concurrent container

    • ConcurrentHashMap

      • put execution flow?
      • How to expand?
      • Difference between ConcurrentHashMap and Hashtable?
    • CopyOnWrite
    • ConcurrentLinkedQueue
    • blocking queue

      • Blocking queue provided by JDK
      • principle

This article has been included in the github repository, which is used to share high-frequency interview questions of Internet companies and a summary of Java core knowledge , including Java basics, concurrency, MySQL, Springboot, MyBatis, Redis, RabbitMQ, etc. It is necessary for interviews! Welcome everyone star!

github address: https://github.com/Tyson0314/Java-learning

What are the common collections?

The Java collection class is mainly derived from two interfaces: Collection and Map . Collection has three sub-interfaces: List, Set, and Queue.

The Java collection framework diagram is as follows:

List represents an ordered and repeatable collection, which can be accessed directly according to the index of the element; Set represents an unordered non-repeatable collection, which can only be accessed according to the element itself; Queue is a queue collection. Map represents a collection of stored key-value pairs, and the value can be accessed according to the key of the element.

Commonly used implementation classes in the collection system include ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap and other implementation classes.

The difference between List, Set and Map

  • List accesses elements by index, ordered, elements are allowed to be repeated, and multiple nulls can be inserted;
  • Set cannot store duplicate elements, unordered, only one null is allowed;
  • Map saves the key-value pair mapping;
  • The bottom layer of List is implemented in two ways: array and linked list; Set and Map containers are implemented in two ways: hash storage and red-black tree;
  • Set is implemented based on Map, and the element value in Set is the key value of Map.

ArrayList understand?

ArrayList is a dynamic array, and its capacity can grow dynamically. Before adding a large number of elements, the application can increase the capacity of the ArrayList ensureCapacity ArrayList inherits AbstractList and implements the List interface.

The expansion mechanism of ArrayList?

The essence of ArrayList expansion is to calculate the size of the new expanded array, instantiate it, and copy the original array content to the new array. By default, the new capacity will be 1.5 times the original capacity . Take JDK1.8 as an example to illustrate:

public boolean add(E e) {
    //判断是否可以容纳e,若能,则直接添加在末尾;若不能,则进行扩容,然后再把e添加在末尾
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //将e添加到数组末尾
    elementData[size++] = e;
    return true;
    }

// 每次在add()一个元素时,arraylist都需要对这个list的容量进行一个判断。通过ensureCapacityInternal()方法确保当前ArrayList维护的数组具有存储新元素的能力,经过处理之后将元素存储在数组elementData的尾部

private void ensureCapacityInternal(int minCapacity) {
      ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
        //如果传入的是个空数组则最小容量取默认容量与minCapacity之间的最大值
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    
  private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // 若ArrayList已有的存储能力满足最低存储要求,则返回add直接添加元素;如果最低要求的存储能力>ArrayList已有的存储能力,这就表示ArrayList的存储能力不足,因此需要调用 grow();方法进行扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }


private void grow(int minCapacity) {
        // 获取elementData数组的内存空间长度
        int oldCapacity = elementData.length;
        // 扩容至原来的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //校验容量是否够
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //若预设值大于默认的最大值,检查是否溢出
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 调用Arrays.copyOf方法将elementData数组指向新的内存空间
         //并将elementData的数据复制到新的内存空间
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

How do I remove an element while iterating through an ArrayList?

Foreach removal causes fail-fast problems, you can use the iterator's remove() method.

Iterator itr = list.iterator();
while(itr.hasNext()) {
      if(itr.next().equals("jay") {
        itr.remove();
      }
}

Difference between Arraylist and Vector

  1. When the memory is insufficient, ArrayList is expanded by 50% + 1 by default, and Vector is expanded by 1 times by default.
  2. Vector belongs to the thread-safe level, but in most cases, Vector is not used because the efficiency of vector operation is relatively low.

Difference between Arraylist and LinkedList

  1. ArrayList is implemented based on dynamic array; LinkedList is implemented based on linked list.
  2. For random index access get and set methods, ArrayList is faster than LinkedList. Because ArrayList finds elements directly through the array subscript; LinkedList needs to move the pointer to traverse each element until it is found.
  3. Adding and removing elements, LinkedList is faster than ArrayList. Because ArrayList may expand and copy the array when adding and deleting elements; LinkedList takes time to instantiate the object, and only needs to modify the pointer.

HashMap

HashMap is implemented by using array + linked list + red-black tree (JDK1.8 adds the red-black tree part). When the length of the linked list is greater than 8 (TREEIFY_THRESHOLD), the linked list will be converted into a red-black tree, and the number of nodes in the red-black tree is less than 6 ( UNTREEIFY_THRESHOLD) is converted into a linked list to prevent frequent conversions.

What are the ways to resolve hash conflicts? Which HashMap is used?

The methods to solve Hash conflict are: open addressing method, re-hash method, chain address method. The chain address method is used in HashMap.

  • The basic idea of open addressing method is that if p=H(key) event of a conflict, places p basis, hash again, p1=H(p) , if p1 conflict again, places p1-based, and so on, until you find a hash address that does not conflict pi . Therefore, the length of the hash table required by the open addressing method is greater than or equal to the element to be stored, and because there is a second hash, can only mark the deleted node, but cannot actually delete the node.
  • The re-hash method provides multiple different hash functions. When R1=H1(key1) R2=H2(key1) until there is no conflict. Although this is not easy to generate heap, it increases the calculation time.
  • The chain address method forms a singly linked list of synonyms with elements with the same hash value, and stores the head pointer of the singly linked list in the i-th unit of the hash table. Search, insertion and deletion are mainly performed in the synonym linked list. The linked list method is suitable for frequent insertions and deletions.

The hash algorithm used?

Hash algorithm: take the hashCode value of the key, high-order operation, and modulo operation.

h=key.hashCode() //第一步 取hashCode值
h^(h>>>16)  //第二步 高位参与运算,减少冲突
return h&(length-1);  //第三步 取模运算

In the implementation of JDK1.8, the algorithm of high-order operations is optimized, which is realized by the high-order 16-bit XOR of hashCode() or the low-order 16-bit: this can ensure that both high and low bits are involved when the array is relatively small. In the calculation of Hash, conflicts can be reduced without too much overhead.

Expansion process?

1.8 Capacity expansion mechanism: When the number of elements is greater than the threshold, the capacity will be expanded, and an array with twice the capacity will be used to replace the original array. Copy the elements of the original array to the new array by tail insertion. After the 1.8 expansion, the relative position of the linked list elements does not change, but after the 1.7 expansion, the linked list elements will be inverted.

1.7 The new node of the linked list adopts the head insertion method, so when a thread expands and migrates elements, the order of the elements will be changed, causing the elements in the two threads to point to each other to form a circular linked list. 1.8 The tail insertion method is adopted to avoid This happens.

After recalculating the hash of the elements of the original array, because the array capacity n is doubled, the mask range of n-1 is 1 bit more in the high order. In the process of element copying, there is no need to recalculate the position of the element in the array. You only need to check whether the new bit of the original hash value is 1 or 0. If it is 0, the index does not change. If it is 1, the index becomes "original index + oldCap" ( e.hash & (oldCap - 1) == 0 ). This saves the time to recalculate the hash value, and since the newly added 1bit is 0 or 1 can be considered random, the resize process will evenly distribute the previous conflicting nodes to the new bucket.

put method process?

  1. If the table is not initialized, the initialization process is performed first
  2. Calculate the index of the key using the hash algorithm
  3. Determine if there is an element at the index, and insert it directly if not
  4. If there is an element at the index, traverse and insert, there are two cases, one is a linked list form, it is directly traversed to the end to insert, the other is a red-black tree, which is inserted according to the red-black tree structure
  5. If the number of linked lists is greater than the threshold of 8, it must be converted into a red-black tree structure
  6. After the addition is successful, it will check whether expansion is required

What are the characteristics of a red-black tree?

  • Each node is either black or red.
  • The root node is black.
  • Each leaf node (NIL) is black.
  • If a node is red, its children must be black.
  • All paths from a node to the node's descendants contain the same number of black nodes.

Why use a red-black tree instead of an AVL tree?

ConcurrentHashMap will lock when put, using red-black tree to insert faster, can reduce the time waiting for lock release. The red-black tree is an optimization of the AVL tree. It only requires partial balance. The non-strict balance is exchanged for the reduction of the number of rotations when adding and deleting nodes, which improves the performance of insertion and deletion.

When resolving hash conflicts, why choose to use linked list first, and then switch to red-black tree?

Because the red-black tree needs to be left-handed, right-handed, and changed to maintain balance, while the singly linked list does not. When the number of elements is less than 8, the linked list structure can guarantee the query performance. When the number of elements is greater than 8, the search time complexity of the red-black tree is O(logn), and the linked list is O(n). At this time, the red-black tree is needed to speed up the query speed, but the efficiency of inserting and deleting nodes becomes slower. . If the red-black tree structure is used at the beginning, there are too few elements, and the efficiency of inserting and deleting nodes is relatively slow, which wastes performance.

Why is the length of HashMap a power of 2?

The range of Hash values is relatively large. Before use, you need to take the modulo operation on the length of the array, and the remainder is the location where the element is stored, that is, the corresponding array subscript. The index of this array is calculated as (n - 1) & hash . Set the length of the HashMap to the power of 2, so that the (n - 1)&hash bit operation can be used instead of the % operation to improve performance.

What is the default load factor for HashMap? Why 0.75?

Let's take a look at the default constructor of HashMap:

int threshold;             // 容纳键值对的最大值
final float loadFactor;    // 负载因子
int modCount;  
int size;  

The initial length of Node[] table is 16, and the default loadFactor is 0.75. 0.75 is a balanced choice for space and time efficiency. According to Poisson distribution, loadFactor takes 0.75 to minimize collision. Generally, it will not be modified, except in special cases of time and space:

  • If there is a lot of memory space and high time efficiency is required, the value of Load factor can be reduced.
  • If the memory space is tight and the time efficiency is not high, you can increase the value of the load factor loadFactor, which can be greater than 1.

What is generally used as the key of HashMap?

Generally, immutable classes such as Integer and String are used as HashMap keys. String class is more commonly used.

  • Because String is immutable, the hashcode is cached when it is created and does not need to be recomputed. This is why the keys in HashMap often use strings.
  • The equals() and hashCode() methods are used to obtain objects, and the Integer and String classes have already rewritten the hashCode() and equals() methods, so you don't need to override these two methods yourself.

Why is HashMap not thread safe?

  • The infinite loop of expansion under multi-threading. The HashMap in JDK1.7 uses the head insertion method to insert elements. In a multi-threaded environment, the expansion may lead to the circular linked list , forming an infinite loop.
  • In JDK1.8, in a multi-threaded environment, data overwrites .

Difference between HashMap and HashTable?

Both HashMap and Hashtable implement the Map interface.

  1. HashMap can accept null keys and values, and key-value pairs with null keys are placed in the linked list of the head node with subscript 0, but Hashtable cannot.
  2. HashMap is not thread safe, HashTable is thread safe. Jdk1.5 provides ConcurrentHashMap, which is a replacement for HashTable.
  3. Many methods of Hashtable are synchronized methods, which are slower than HashMap in a single-threaded environment.
  4. The use of hash values is different, HashTable directly uses the hashCode of the object. And HashMap recalculates the hash value.

The underlying principle of LinkedHashMap?

HashMap is unordered, the order of elements obtained by iterating HashMap is not the order in which they were originally placed in HashMap, that is, their insertion order cannot be maintained.

LinkedHashMap inherits from HashMap, which is a fusion of HashMap and LinkedList, and has the characteristics of both. Each put operation inserts the entry at the end of the doubly linked list.

linkedhashmap

Talk about TreeMap?

TreeMap is a collection of Maps that can compare the size of elements, and sorts the incoming keys by size. You can use the natural ordering of the elements, or you can use a custom comparator in the collection to sort.

public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable {
}

The inheritance structure of TreeMap:

TreeMap:

  1. TreeMap is an ordered key-value collection, implemented through a red-black tree. Sorts according to the natural order of the keys or according to the provided Comparator.
  2. TreeMap inherits AbstractMap, implements the NavigableMap interface, supports a series of navigation methods, and returns the closest match given a specific search target. For example, floorEntry() and ceilingEntry() return Map.Entry() objects less than or equal to and greater than or equal to the given key, respectively, or null if they do not exist. lowerKey(), floorKey, ceilingKey, higherKey() only return the associated key.

The underlying principle of HashSet?

HashSet is implemented based on HashMap. The elements put into the HashSet are actually stored by the key of the HashMap, and the value of the HashMap stores a static Object object.

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {
    static final long serialVersionUID = -5024744406713321676L;

    private transient HashMap<E,Object> map; //基于HashMap实现
    //...
}

Difference between HashSet, LinkedHashSet and TreeSet?

HashSet is Set main interface implementation class, HashSet bottom is HashMap , thread-safe, a null value may be stored;

LinkedHashSet is HashSet and can be traversed in the order of addition;

TreeSet bottom layer of 061df7999f1c8c uses a red-black tree, which can be traversed in the order in which elements are added, and the sorting method can be customized.

What is fail fast?

fast-fail is a failure mechanism for Java collections. When multiple threads operate on the same collection, it is possible to generate fast-fail events. For example: when thread a is traversing the collection through iterator, another thread b modifies the content of the collection. At this time, modCount (recording the number of modifications in the collection operation process) will increase by 1, which is not equal to expectedModCount, then when thread a accesses the collection, ConcurrentModificationException will be thrown, resulting in fast-fail event. Modifying the collection while traversing will also generate a fast-fail event.

Solution:

  • Use the Collections.synchronizedList method or add synchronized where you modify the contents of the collection. In this case, the synchronization lock for adding or deleting collection content will block the traversal operation and affect performance.
  • Use CopyOnWriteArrayList to replace ArrayList. When modifying the CopyOnWriteArrayList, a new array will be copied, the new array will be operated, and the reference will be moved to the new array after the operation is completed.

What is failsafe?

A collection container that adopts a failsafe mechanism does not directly access the collection content during traversal, but first copies the original collection content and traverses the copied collection. The containers under the java.util.concurrent package are all safe to fail, and can be used and modified concurrently under multiple threads.

Principle : Since the copy of the original collection is traversed during iteration, the modifications made to the original collection during the traversal process cannot be detected by the iterator, so the Concurrent Modification Exception will not be triggered.

Disadvantage : The advantage of copying content is to avoid Concurrent Modification Exception, but in the same way, the iterator cannot access the modified content, that is: the iterator traverses the copy of the collection obtained at the moment of the traversal, and when traversing Modifications that occur to the original collection during the iterator are not known.

Talk about ArrayDeque?

ArrayDeque implements a double-ended queue, which is implemented internally using a circular array with a default size of 16. Its features are:

  1. Adding and removing elements at both ends is more efficient
  2. The efficiency of finding and deleting based on element content is relatively low.
  3. There is no concept of index position, and you cannot operate based on index position.

Both ArrayDeque and LinkedList implement the Deque interface. If you only need to operate from both ends, ArrayDeque is more efficient. If you need to operate according to the index position at the same time, or often need to insert and delete in the middle (LinkedList has a corresponding api, such as add(int index, E e)), you should choose LinkedList.

ArrayDeque and LinkedList are both thread-unsafe and can be converted to thread synchronization using synchronizedXxx() in the Collections tool class.

Which collection classes are thread safe? Which are unsafe?

Linear-safe collection classes:

  • Vector: There are more synchronization mechanisms than ArrayList.
  • Hashtable。
  • ConcurrentHashMap: is an efficient and thread-safe collection.
  • Stack: Stack, also thread-safe, inherits from Vector.

Linearly unsafe collection classes:

  • Hashmap
  • Arraylist
  • LinkedList
  • HashSet
  • TreeSet
  • TreeMap

What is Iterator?

The Iterator pattern uses the same logic to traverse a collection. It can abstract the access logic from different types of collection classes, traverse the elements of the collection without knowing the internal implementation of the collection, and use the interface provided by Iterator to traverse. It is more secure because it guarantees that a ConcurrentModificationException will be thrown when the currently traversed collection element is changed.

public interface Collection<E> extends Iterable<E> {
    Iterator<E> iterator();
}

There are three main methods: hasNext(), next() and remove().

What is the difference between Iterator and ListIterator?

ListIterator is an enhanced version of Iterator.

  • ListIterator traversal can be reversed because there are previous() and hasPrevious() methods, while Iterator cannot.
  • ListIterator has add() method, which can add objects to List, but Iterator cannot.
  • ListIterator can locate the current index position because there are nextIndex() and previousIndex() methods, but Iterator cannot.
  • ListIterator can realize the modification of the object, and the set() method can be realized. Iierator can only be traversed, not modified.
  • ListIterator can only be used to traverse List and its subclasses, Iterator can be used to traverse all collections.

concurrent container

Most of these containers provided by the JDK are in the java.util.concurrent package.

  • ConcurrentHashMap: Thread-safe HashMap
  • CopyOnWriteArrayList: Thread-safe List, which has very good performance in the case of reading more and writing less, far better than Vector.
  • ConcurrentLinkedQueue: Efficient concurrent queue, implemented using linked list. It can be seen as a thread-safe LinkedList, which is a non-blocking queue.
  • BlockingQueue: Blocking queue interface, which is implemented internally by JDK through linked lists and arrays. Ideal for use as a channel for data sharing.
  • ConcurrentSkipListMap: skip list. Use the skip table data structure for fast lookups.

ConcurrentHashMap

In a multi-threaded environment, using Hashmap for put operation will cause an infinite loop. ConcurrentHashMap that supports multi-threading should be used.

JDK1.8 ConcurrentHashMap cancels the segment segment lock, and uses CAS and synchronized to ensure concurrency safety. The data structure adopts array + linked list/red-black binary tree. Synchronized only locks the first node of the current linked list or red-black binary tree. Compared with 1.7 locking the HashEntry array, the lock granularity is smaller and supports higher concurrency. When the length of the linked list is too long, Node will be converted into TreeNode to improve the search speed.

put execution flow?

Segments need to be locked when put to ensure concurrency safety. There is no lock when calling get, because the node array member val and pointer next are modified with volatile, and the changed value will be immediately flushed to the main memory to ensure visibility, and the node array table is also modified with volatile to ensure that it is running Processes have visibility to other threads.

transient volatile Node<K,V>[] table;

static class Node<K,V> implements Map.Entry<K,V> {
    volatile V val;
    volatile Node<K,V> next;
}

put operation process:

  1. If the table is not initialized, the initialization process is performed first
  2. Use the hash algorithm to calculate the position of the key
  3. If this position is empty, it will be inserted directly by CAS. If it is not empty, the node will be taken out to
  4. If the hash value of the extracted node is MOVED(-1), it means that the array is currently being expanded and copied to a new array, and the current thread will also help copy
  5. If this node is not empty and is not expanding, it will be locked and added through synchronized. There are two cases here, one is in the form of a linked list, and the same key is directly traversed to the end to insert or overwrite, one is If it is a red-black tree, insert it according to the red-black tree structure
  6. If the number of linked lists is greater than the threshold of 8, it will be converted into a red-black tree structure or expanded (table length is less than 64)
  7. After the addition is successful, it will check whether expansion is required

How to expand?

A step size is set in the array expansion transfer method, indicating the length of the array processed by a thread, and the minimum value is 16. Only one thread will copy and move it within a step range.

Difference between ConcurrentHashMap and Hashtable?

  1. Hashtable achieves multi-threaded synchronization by using the synchronized modification method. Therefore, the synchronization of Hashtable will lock the entire array. In the case of high concurrency, the performance will be very poor. ConcurrentHashMap uses more fine-grained locking to improve efficiency in concurrent situations. Note: Synchronized containers (synchronized containers) also use the synchronized keyword to achieve thread safety, and all data will be locked when used.
  2. The default size of Hashtable is 11. When the threshold is reached, the capacity is expanded each time according to the following formula: newCapacity = oldCapacity * 2 + 1. The default size of ConcurrentHashMap is 16, and the capacity is doubled when expanding.

CopyOnWrite

Copy-on-write. When we add elements to the container, we do not directly add to the container, but first copy the current container, copy a new container, and then add elements to the new container. After adding the elements, point the reference of the original container to new container. The advantage of this is that the CopyOnWrite container can be read concurrently without locking, because the current container will not be modified.

    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock(); //add方法需要加锁
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1); //复制新数组
            newElements[len] = e;
            setArray(newElements); //原容器的引用指向新容器
            return true;
        } finally {
            lock.unlock();
        }
    }

Since JDK1.5, the Java concurrent package provides two concurrent containers implemented using the CopyOnWrite mechanism, which are CopyOnWriteArrayList and CopyOnWriteArraySet.

When the add method in CopyOnWriteArrayList is added, it needs to be locked to ensure synchronization and avoid multiple copies when writing by multiple threads. There is no need to lock when reading. If other threads are adding data to CopyOnWriteArrayList when reading, the old data can still be read.

Cons:

  • Memory usage issue. Due to the copy-on-write mechanism of CopyOnWrite, when a write operation is performed, the memory of two objects will reside in the memory at the same time.
  • The CopyOnWrite container cannot guarantee the real-time consistency of data, and may read old data.

ConcurrentLinkedQueue

non-blocking queue. Efficient concurrent queues, implemented using linked lists. It can be seen as a thread-safe LinkedList, implemented through CAS operations.

If the cost of locking the queue is high, it is suitable to use the lock-free ConcurrentLinkedQueue instead. It is suitable for scenarios with relatively high performance requirements and multiple threads reading and writing to the queue at the same time.

Several main methods in non-blocking queues:
add(E e) : Insert element e at the end of the queue, if the insertion is successful, it will return true; if the insertion fails (that is, the queue is full), an exception will be thrown;
remove() : remove the first element of the queue, if the removal is successful, it will return true; if the removal fails (the queue is empty), an exception will be thrown;
offer(E e) : insert element e at the end of the queue, return true if the insertion is successful; return false if the insertion fails (that is, the queue is full);
poll() : remove and get the first element of the team, if successful, return the first element of the team; otherwise, return null;
peek() : Get the first element of the team, if successful, return the first element of the team; otherwise, return null

For non-blocking queues, it is generally recommended to use offer, poll and peek methods, and add and remove methods are not recommended. Because the three methods of offer, poll and peek can use the return value to determine whether the operation is successful or not, but the add and remove methods cannot achieve such an effect.

blocking queue

The blocking queue is an important data structure under the java.util.concurrent package. BlockingQueue provides a thread-safe queue access method: when the blocking queue is inserting data, if the queue is full, the thread will block and wait until the queue is not full; from blocking When the queue is fetching data, if the queue is empty, the thread will block and wait until the queue is not empty. The implementation of many advanced synchronization classes under the concurrent package is based on BlockingQueue. BlockingQueue is suitable for use as a channel for data sharing.

Queues using blocking algorithms can be implemented with one lock (the same lock for enqueue and dequeue) or two locks (different locks for enqueue and dequeue).

The difference between a blocking queue and a general queue is that:

  1. Multithreading support, multiple threads can safely access the queue
  2. Blocking operation, when the queue is empty, the consumer thread will block until the queue is not empty; when the queue is full, the production thread will block until the queue is full.

method

method\processing methodThrow an exceptionreturn special valuekeep blockingtime out
Insert methodadd(e)offer(e)put(e)offer(e,time,unit)
removal methodremove()poll()take()poll(time,unit)
Inspection Methodelement()peek()unavailableunavailable

Blocking queue provided by JDK

JDK 7 provides 7 blocking queues, as follows

1、ArrayBlockingQueue

Bounded blocking queue, the bottom layer is implemented by array. Once an ArrayBlockingQueue is created, its capacity cannot be changed. Its concurrency control is controlled by reentrant locks. Whether it is an insert operation or a read operation, the lock needs to be acquired to perform the operation. This queue sorts elements on a first-in-first-out (FIFO) basis. By default, the fairness of the thread access queue cannot be guaranteed. The parameter fair can be used to set whether the thread accesses the queue fairly. To ensure fairness, throughput is usually reduced.

private static ArrayBlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<Integer>(10,true);//fair

2、LinkedBlockingQueue

LinkedBlockingQueue is a bounded blocking queue implemented with a singly linked list, which can be used as an unbounded queue or a bounded queue. Usually when you create a LinkedBlockingQueue object, you specify the maximum capacity of the queue. The default and maximum length of this queue is Integer.MAX_VALUE . This queue sorts elements on a first-in, first-out basis. Has higher throughput compared to ArrayBlockingQueue.

3、PriorityBlockingQueue

unbounded blocking queue with priority support. By default, elements are sorted in ascending natural order. You can also customize the class to implement the compareTo() method to specify the element sorting rules, or when initializing the PriorityBlockingQueue, specify the construction parameter Comparator to sort.

PriorityBlockingQueue can only specify the initial queue size. When inserting elements later, if the space is not enough, will automatically expand .

Thread-safe version of PriorityQueue. Null values cannot be inserted, and at the same time, the objects inserted into the queue must be of comparable size (comparable), otherwise a ClassCastException will be reported. Its insert operation put method will not block, because it is an unbounded queue (take method will block when the queue is empty).

4、DelayQueue

An unbounded blocking queue that supports delayed fetching of elements. Queues are implemented using PriorityBlockingQueue. The elements in the queue must implement the Delayed interface, and you can specify how long to get the current element from the queue when you create the element. Elements can be drawn from the queue only when the delay expires.

5、SynchronousQueue

A blocking queue that does not store elements, each put must wait for a take operation, otherwise it cannot continue to add elements. Support for fair access queues.

SynchronousQueue can be seen as a passer, responsible for passing the data processed by the producer thread directly to the consumer thread. The queue itself does not store any elements and is ideal for transitive scenarios. The throughput of SynchronousQueue is higher than LinkedBlockingQueue and ArrayBlockingQueue.

6、LinkedTransferQueue

An unbounded blocking TransferQueue queue consisting of a linked list structure. Compared with other blocking queues, there are more tryTransfer and transfer methods.

Transfer method: If there is currently a consumer waiting to receive an element (take or poll method with a time limit), transfer can immediately transfer the element passed in by the producer to the consumer. If there is no consumer waiting to receive the element, put the element in the tail node of the queue, and wait until the element is consumed by the consumer before returning.

tryTransfer method: used to test whether the element passed in by the producer can be directly passed to the consumer. Returns false if no consumers are waiting. The difference from the above method is that this method returns immediately regardless of whether the consumer receives it or not. The transfer method must wait until the consumer consumes it before returning.

principle

JDK implements blocking queues using the notification pattern. The so-called notification mode means that when the producer adds elements to the full queue, the producer will be blocked. When the consumer consumes an element in the queue, the producer will be notified that the current queue is available.

ArrayBlockingQueue uses Condition to implement:

private final Condition notEmpty;
private final Condition notFull;

public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <= 0)
        throw new IllegalArgumentException();
    this.items = new Object[capacity];
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();
    notFull =  lock.newCondition();
}

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0) // 队列为空时,阻塞当前消费者
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}

public void put(E e) throws InterruptedException {
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == items.length)
            notFull.await();
        enqueue(e);
    } finally {
        lock.unlock();
    }
}

private void enqueue(E x) {
    final Object[] items = this.items;
    items[putIndex] = x;
    if (++putIndex == items.length)
          putIndex = 0;
     count++;
     notEmpty.signal(); // 队列不为空时,通知消费者获取元素
}

If the article is useful to you, , support it~

I am Dabin, I am not a professional transcoding, and the school has recruited a number of Internet companies to offer offers, focusing on sharing Java technology dry goods, welcome to pay attention~


程序员大彬
468 声望489 粉丝

非科班转码,个人网站:topjavaer.cn