Java并发 - J.U.C并发容器类 list、set、queue

叁歲伎倆 2023-10-14 20:55 122阅读 0赞
1. List
ArrayList
  • 本质就是一个数组
  • 初识化大小默认为 10

    /**

    1. * Default initial capacity.
    2. */
    3. private static final int DEFAULT_CAPACITY = 10;
  • 每次扩容后大小变为原大小的1.5倍

    private void grow(int minCapacity) {

    1. // overflow-conscious code
    2. int oldCapacity = elementData.length;
    3. int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容为1.5倍大小
    4. if (newCapacity - minCapacity < 0)
    5. newCapacity = minCapacity;
    6. if (newCapacity - MAX_ARRAY_SIZE > 0)
    7. newCapacity = hugeCapacity(minCapacity);
    8. // minCapacity is usually close to size, so this is a win:
    9. elementData = Arrays.copyOf(elementData, newCapacity);
    10. }
  • 使用 for(Object o : list) 迭代器进行迭代循环的时候不应该对列表 list 进行新增或者删除操作,否则会报ConcurrentModificationException 异常,原因是因为迭代过程中会检查变量数量和期望的数量是否一致。

如以下操作就会报错

  1. int i=0;
  2. for (Object o : list) {
  3. if(i==0) list.add("neco");
  4. i++;
  5. }

抛出异常的代码

  1. final void checkForComodification() {
  2. if (modCount != expectedModCount)
  3. throw new ConcurrentModificationException();
  4. }
LinkedList
  • 本质就是一个链表,没有什么特殊的内容
  • 里边的Node是支持双向链表的

    private static class Node {

    1. E item;
    2. Node<E> next;
    3. Node<E> prev;
    4. Node(Node<E> prev, E element, Node<E> next) {
    5. this.item = element;
    6. this.next = next;
    7. this.prev = prev;
    8. }
    9. }
CopyOnWriteArrayList
  • 在写的时候复制了一份出来,然后重新写入数据

    /**

    1. * Appends the specified element to the end of this list.
    2. *
    3. * @param e element to be appended to this list
    4. * @return {@code true} (as specified by {@link Collection#add})
    5. */
    6. public boolean add(E e) {
    7. final ReentrantLock lock = this.lock;
    8. lock.lock();
    9. try {
    10. Object[] elements = getArray();
    11. int len = elements.length;
    12. Object[] newElements = Arrays.copyOf(elements, len + 1);
    13. newElements[len] = e;
    14. setArray(newElements);
    15. return true;
    16. } finally {
    17. lock.unlock();
    18. }
    19. }
  • 确保了读写可以同步进行,但是可能会有脏读的情况

  • 在多读少写的情况下可以使用

CopyOnWriteArrayList容器即写时复制的容器。和ArrayList比较,优点是并发安全缺点有两个:
1、多了内存占用:写数据是copy一份完整的数据,单独进行操作。占用双份内存。
2、数据一致性:数据写完之后,其他线程不一定是马上读取到最新内容。

format_png

2. Set 集合

和List比较:不会重复


























实现 原理 特点
HashSet 基于HashMap实现 非线程安全
CopyOnWriteArraySet 基于CopyOnWriteArrayList 线程安全
TreeSet 基于TreeMap 线程安全,有序,查询快
HashSet

内部的实现本质就是一个Map(因为key值不重复),但是只是使用了Key,对于value无所谓

  1. /**
  2. * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
  3. * default initial capacity (16) and load factor (0.75).
  4. */
  5. public HashSet() {
  6. map = new HashMap<>();
  7. }
  8. /**
  9. * Adds the specified element to this set if it is not already present.
  10. * More formally, adds the specified element <tt>e</tt> to this set if
  11. * this set contains no element <tt>e2</tt> such that
  12. * <tt>(e==null ? e2==null : e.equals(e2))</tt>.
  13. * If this set already contains the element, the call leaves the set
  14. * unchanged and returns <tt>false</tt>.
  15. *
  16. * @param e element to be added to this set
  17. * @return <tt>true</tt> if this set did not already contain the specified
  18. * element
  19. */
  20. public boolean add(E e) {
  21. return map.put(e, PRESENT)==null;
  22. }
  23. // Dummy value to associate with an Object in the backing Map
  24. private static final Object PRESENT = new Object();
CopyOnWriteArraySet

内部的本质是一个 CopyOnWriteArrayList,通过判断是否存在来确定是否放入数据

  1. /**
  2. * Creates an empty set.
  3. */
  4. public CopyOnWriteArraySet() {
  5. al = new CopyOnWriteArrayList<E>();
  6. }
  7. /**
  8. * Adds the specified element to this set if it is not already present.
  9. * More formally, adds the specified element {@code e} to this set if
  10. * the set contains no element {@code e2} such that
  11. * <tt>(e==null ? e2==null : e.equals(e2))</tt>.
  12. * If this set already contains the element, the call leaves the set
  13. * unchanged and returns {@code false}.
  14. *
  15. * @param e element to be added to this set
  16. * @return {@code true} if this set did not already contain the specified
  17. * element
  18. */
  19. public boolean add(E e) {
  20. return al.addIfAbsent(e);
  21. }
  22. /**
  23. * Appends the element, if not present.
  24. *
  25. * @param e element to be added to this list, if absent
  26. * @return {@code true} if the element was added
  27. */
  28. public boolean addIfAbsent(E e) {
  29. Object[] snapshot = getArray();
  30. return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
  31. addIfAbsent(e, snapshot);
  32. }
  33. /**
  34. * A version of addIfAbsent using the strong hint that given
  35. * recent snapshot does not contain e.
  36. */
  37. private boolean addIfAbsent(E e, Object[] snapshot) {
  38. final ReentrantLock lock = this.lock;
  39. lock.lock();
  40. try {
  41. Object[] current = getArray();
  42. int len = current.length;
  43. if (snapshot != current) {
  44. // Optimize for lost race to another addXXX operation
  45. int common = Math.min(snapshot.length, len);
  46. for (int i = 0; i < common; i++)
  47. if (current[i] != snapshot[i] && eq(e, current[i]))
  48. return false;
  49. if (indexOf(e, current, common, len) >= 0)
  50. return false;
  51. }
  52. Object[] newElements = Arrays.copyOf(current, len + 1);
  53. newElements[len] = e;
  54. setArray(newElements);
  55. return true;
  56. } finally {
  57. lock.unlock();
  58. }
  59. }
TreeSet

本质是一个TreeMap,但是也只用到了Key值,Value值没有什么意义。

  1. /**
  2. * Constructs a new, empty tree set, sorted according to the
  3. * natural ordering of its elements. All elements inserted into
  4. * the set must implement the {@link Comparable} interface.
  5. * Furthermore, all such elements must be <i>mutually
  6. * comparable</i>: {@code e1.compareTo(e2)} must not throw a
  7. * {@code ClassCastException} for any elements {@code e1} and
  8. * {@code e2} in the set. If the user attempts to add an element
  9. * to the set that violates this constraint (for example, the user
  10. * attempts to add a string element to a set whose elements are
  11. * integers), the {@code add} call will throw a
  12. * {@code ClassCastException}.
  13. */
  14. public TreeSet() {
  15. this(new TreeMap<E,Object>());
  16. }
  17. /**
  18. * Adds the specified element to this set if it is not already present.
  19. * More formally, adds the specified element {@code e} to this set if
  20. * the set contains no element {@code e2} such that
  21. * <tt>(e==null ? e2==null : e.equals(e2))</tt>.
  22. * If this set already contains the element, the call leaves the set
  23. * unchanged and returns {@code false}.
  24. *
  25. * @param e element to be added to this set
  26. * @return {@code true} if this set did not already contain the specified
  27. * element
  28. * @throws ClassCastException if the specified object cannot be compared
  29. * with the elements currently in this set
  30. * @throws NullPointerException if the specified element is null
  31. * and this set uses natural ordering, or its comparator
  32. * does not permit null elements
  33. */
  34. public boolean add(E e) {
  35. return m.put(e, PRESENT)==null;
  36. }
  37. // Dummy value to associate with an Object in the backing Map
  38. private static final Object PRESENT = new Object();

SET接口没有所谓的有序还是无序。 TreeSet是有序的,此有序是说读取数据的顺序和插入数据的顺序一样。 HashSet无序? 此无序说的是读取数据的顺序不一定和插入数据的顺序一样。

3. Queue
Queue API

Queue -队列数据结构的实现。分为阻塞队列和非阻塞队列。下列的蓝色区块,为阻塞队列特有的方法。

format_png 1

阻塞是通过condition来实现的,可参考 Java并发 - Lock接口

  • ArrayBlockingQueue 阻塞
  • LinkedBlockingQueue 阻塞
  • ArrayQueue 非阻塞
  • LinkedQueue 非阻塞

发表评论

表情:
评论列表 (有 0 条评论,122人围观)

还没有评论,来说两句吧...

相关阅读

    相关 并发容器

    并发类容器 JDK 1.5之后,提供了多种并发类容器 来代替同步类容器,改善性能 同步类容器 状态都是串行化的,虽然,实现了线程安全 但是,严重降低了并发性