7.1 线程安全的同步容器类

1 .通过synchronizedSortedSet静态方法包装出一个同步容器

  1. package com.crazymakercircle.syncontainer;
  2. // 省略import
  3. public class CollectionsDemo
  4. {
  5. public static void main(String[] args) throws InterruptedException
  6. {
  7. // 创建一下基础的有序集合
  8. SortedSet<String> elementSet = new TreeSet<String>();
  9. // 增加元素
  10. elementSet.add("element 1");
  11. elementSet.add("element 2");
  12. // 将 elementSet 包装成一个同步容器
  13. SortedSet sorset = Collections.synchronizedSortedSet(elementSet);
  14. // 输出容器中的元素
  15. System.out.println("SortedSet is :" + sorset);
  16. CountDownLatch latch=new CountDownLatch(5);
  17. for (int i = 0; i < 5; i++)
  18. {
  19. int finalI = i;
  20. ThreadUtil.getCpuIntenseTargetThreadPool()
  21. .submit(() ->{
  22. // 向同步容器中增加一个元素
  23. sorset.add("element " + (3 + finalI));
  24. Print.tco("add element"+ (3 + finalI));
  25. latch.countDown();
  26. });
  27. }
  28. latch.await();
  29. // 输出容器中的元素
  30. System.out.println("SortedSet is :" + sorset);
  31. }
  32. }
  1. java.util.Collections所提供的同步包装方法
  2. 同步容器面临的问题

    7.2 JUC高并发容器

    7.3 CopyOnWriteArrayList

    7.3.1 CopyOnWriteArrayList的使用

    前面讲到,Collections可以将基础容器包装为线程安全的同步容器,但是这些同步容器包装类在进行元素迭代时并不能进行元素添加操作。下面是一个简单的例子: ```java package com.crazymakercircle.lockfree; // 省略import public class CopyOnWriteArrayListTest {

    1. //并发操作的执行目标
    2. public static class CocurrentTarget implements Runnable
    3. {
    4. //并发操作的目标队列
    5. List<String> targetList = null;
    6. public CocurrentTarget(List<String> targetList)
    7. {
    8. this.targetList = targetList;
    9. }
    10. @Override
    11. public void run()
    12. {
    13. Iterator<String> iterator = targetList.iterator();
    14. //迭代操作
    15. while (iterator.hasNext())
    16. {
    17. // 在迭代操作时,进行列表的修改
    18. String threadName = currentThread().getName();
    19. Print.tco("开始往同步队列加入线程名称:" + threadName);
    20. targetList.add(threadName);
    21. }
    22. }
    23. }
    24. //测试同步队列:在迭代操作时,进行列表的修改
    25. @Test
    26. public void testSynchronizedList()
    27. {
    28. List<String> notSafeList = asList("a", "b", "c");
    29. List<String> synList = Collections.synchronizedList(notSafeList);
    30. //创建一个执行目标
    31. CocurrentTarget synchronizedListListDemo =
    32. new CocurrentTarget(synList);
    33. //10个线程并发
    34. for (int i = 0; i < 10; i++)
    35. {
    36. new Thread(synchronizedListListDemo , "线程" + i).start();
    37. }
    38. //主线程等待
    39. sleepSeconds(1000);
    40. }

    }

  1. 那么,该如何解决此问题呢?可使用CopyOnWriteArrayList替代Collections.synchronizedList同步包装实例,具体的代码如下:
  2. ```java
  3. package com.crazymakercircle.lockfree;
  4. // 省略import
  5. public class CopyOnWriteArrayListTest
  6. {
  7. //测试CopyOnWriteArrayList
  8. @Test
  9. public void testcopyOnWriteArrayList()
  10. {
  11. List<String> notSafeList = asList("a", "b", "c");
  12. //创建一个CopyOnWriteArrayList队列
  13. List<String> copyOnWriteArrayList = new CopyOnWriteArrayList();
  14. copyOnWriteArrayList.addAll(notSafeList);
  15. //并发执行目标
  16. CocurrentTarget copyOnWriteArrayListDemo =
  17. new CocurrentTarget(copyOnWriteArrayList);
  18. for (int i = 0; i < 10; i++)
  19. {
  20. new Thread(copyOnWriteArrayListDemo, "线程" + i).start();
  21. }
  22. //主线程等待
  23. sleepSeconds(1000);
  24. }
  25. }

7.3.2 CopyOnWriteArrayList的原理

CopyOnWriteArrayList的核心成员如下:

  1. public class CopyOnWriteArrayList<E>
  2. implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
  3. private static final long serialVersionUID = 8673264195747942595L;
  4. /** 对所有的修改器方法进行保护,访问器方法并不需要保护 */
  5. final transient ReentrantLock lock = new ReentrantLock();
  6. /** 内部对象数组,通过 getArray/setArray方法访问 */
  7. private transient volatile Object[] array;
  8. /**
  9. *获取内部对象数组
  10. */
  11. final Object[] getArray() {
  12. return array;
  13. }
  14. /**
  15. *设置内部对象数组
  16. */
  17. final void setArray(Object[] a) {
  18. array = a;
  19. }
  20. // 省略其他代码
  21. }

7.3.3 CopyOnWriteArrayList读取操作

  1. /** 操作内存的引用*/
  2. private transient volatile Object[] array;
  3. public E get(int index) {
  4. return get(getArray(), index);
  5. }
  6. //获取元素
  7. @SuppressWarnings("unchecked")
  8. private E get(Object[] a, int index) {
  9. return (E) a[index];
  10. }
  11. //返回操作内存
  12. final Object[] getArray() {
  13. return array;
  14. }

7.3.4 CopyOnWriteArrayList写入操作

  1. public boolean add(E e) {
  2. final ReentrantLock lock = this.lock;
  3. lock.lock(); // 加锁
  4. try {
  5. Object[] elements = getArray();
  6. int len = elements.length;
  7. // 复制新数组
  8. Object[] newElements = Arrays.copyOf(elements, len + 1);
  9. newElements[len] = e;
  10. setArray(newElements);
  11. return true;
  12. } finally {
  13. lock.unlock(); // 释放锁
  14. }
  15. }

7.3.5 CopyOnWriteArrayList的迭代器实现

  1. static final class COWIterator<E> implements ListIterator<E> {
  2. /**对象数组的快照(snapshot)*/
  3. private final Object[] snapshot;
  4. /** Index of element to be returned by subsequent call to next. */
  5. private int cursor;
  6. private COWIterator(Object[] elements, int initialCursor) {
  7. cursor = initialCursor;
  8. snapshot = elements;
  9. }
  10. public boolean hasNext() {
  11. return cursor < snapshot.length;
  12. }
  13. //下一个元素
  14. public E next() {
  15. if (! hasNext())
  16. throw new NoSuchElementException();
  17. return (E) snapshot[cursor++];
  18. }
  19. }

7.4 BlockingQueue

7.4.1 BlockingQueue的特点

7.4.2 阻塞队列的常用方法

  1. public interface BlockingQueue<E> extends Queue<E> {
  2. //将指定的元素添加到此队列的尾部
  3. //在成功时返回true,如果此队列已满,就抛出IllegalStateException
  4. boolean add(E e);
  5. //非阻塞式添加:将指定的元素添加到此队列的尾部(如果立即可行且不会超过该队列的容量)
  6. //如果该队列已满,就直接返回
  7. boolean offer(E e)
  8. //限时阻塞式添加:将指定的元素添加到此队列的尾部
  9. //如果该队列已满,那么在到达指定的等待时间之前,添加线程会阻塞,等待可用的空间,该方法可中断
  10. boolean offer(E e, long timeout, TimeUnit unit)
  11. throws InterruptedException;
  12. //阻塞式添加:将指定的元素添加到此队列的尾部,如果该队列已满,就一直等待(阻塞)
  13. void put(E e) throws InterruptedException;
  14. //阻塞式删除:获取并移除此队列的头部,如果没有元素就等待(阻塞)
  15. //直到有元素,将唤醒等待线程执行该操作
  16. E take() throws InterruptedException;
  17. //非阻塞式删除:获取并移除此队列的头部,如果没有元素就直接返回null(空)
  18. E poll() throws InterruptedException;
  19. //限时阻塞式删除:获取并移除此队列的头部,在指定的等待时间前一直等待获取元素,超过时间,方法将结束
  20. E poll(long timeout, TimeUnit unit) throws InterruptedException;
  21. //获取但不移除此队列的头元素,没有则抛出异常NoSuchElementException
  22. E element();
  23. //获取但不移除此队列的头元素,如果此队列为空,就返回null
  24. E peek();
  25. //从此队列中移除指定元素,返回删除是否成功
  26. boolean remove(Object o);
  27. }

7.4.3 常见的BlockingQueue

  1. ArrayBlockingQueue
  2. LinkedBlockingQueue
  3. DelayQueue
  4. PriorityBlockingQueue
  5. SynchronousQueue

    7.4.4 ArrayBlockingQueue的基本使用

    ```java package com.crazymakercircle.producerandcomsumer.store; // 省略import public class ArrayBlockingQueuePetStore {

    1. public static final int MAX_AMOUNT = 10; //数据区长度
    2. //共享数据区,类定义
    3. static class DataBuffer<T>
    4. {
    5. //使用阻塞队列保存数据
    6. private ArrayBlockingQueue<T> dataList =
    7. new ArrayBlockingQueue<>(MAX_AMOUNT);
    8. // 向数据区增加一个元素,委托给阻塞队列
    9. public void add(T element) throws Exception
    10. {
    11. dataList.add(element); //直接委托
    12. }
    13. /**
    14. * 从数据区取出一个商品,委托给阻塞队列
    15. */
    16. public T fetch() throws Exception
    17. {
    18. return dataList.take(); //直接委托
    19. }
    20. }
    21. public static void main(String[] args) throws InterruptedException
    22. {
    23. Print.cfo("当前进程的ID是" + JvmUtil.getProcessID());
    24. System.setErr(System.out);
    25. //共享数据区,实例对象
    26. DataBuffer<IGoods> dataBuffer = new DataBuffer<>();
    27. //生产者执行的操作
    28. Callable<IGoods> produceAction = () ->
    29. {
    30. //首先生成一个随机的商品
    31. IGoods goods = Goods.produceOne();
    32. //将商品加上共享数据区
    33. dataBuffer.add(goods);
    34. return goods;
    35. };
    36. //消费者执行的操作
    37. Callable<IGoods> consumerAction = () ->
    38. {
    39. // 从PetStore获取商品
    40. IGoods goods = null;
    41. goods = dataBuffer.fetch();
    42. return goods;
    43. };
    44. // 同时并发执行的线程数
    45. final int THREAD_TOTAL = 20;
    46. // 线程池,用于多线程模拟测试
  1. <a name="DV7Lo"></a>
  2. ## 7.4.5 ArrayBlockingQueue构造器和成员
  3. 1. ArrayBlockingQueue构造器
  4. ```java
  5. //默认非公平阻塞队列
  6. ArrayBlockingQueue queue = new ArrayBlockingQueue(capacity);
  7. //公平阻塞队列
  8. ArrayBlockingQueue queue1 = new ArrayBlockingQueue(capacity,true);
  1. //只带一个capacity参数的构造器
  2. public ArrayBlockingQueue(int capacity) {
  3. this(capacity, false);
  4. }
  5. //带两个参数的构造器
  6. public ArrayBlockingQueue(int capacity, boolean fair) {
  7. if (capacity <= 0)
  8. throw new IllegalArgumentException();
  9. this.items = new Object[capacity];
  10. lock = new ReentrantLock(fair); //根据fair参数构造公平锁/获取非公平锁
  11. notEmpty = lock.newCondition(); //有元素加入,队列为非空
  12. notFull = lock.newCondition(); //有元素被取出,队列为未满
  13. }
  1. ArrayBlockingQueue内部的成员变量 ```java public class ArrayBlockingQueue extends AbstractQueue

    1. implements BlockingQueue<E>, java.io.Serializable {
    2. /** 存储数据的数组 */
    3. final Object[] items;
    4. /**获取、删除元素的索引,主要用于take、poll、peek、remove方法 */
    5. int takeIndex;
    6. /**添加元素的索引,主要用于 put、offer、add方法*/
    7. int putIndex;
    8. /** 队列元素的个数 */
    9. int count;
    10. /** 控制并发访问的显式锁 */
    11. final ReentrantLock lock;
    12. /**notEmpty条件对象,用于通知take线程(消费队列),可执行删除操作 */
    13. private final Condition notEmpty;
    14. /**notFull条件对象,用于通知put线程(生产队列),可执行添加操作 */
    15. private final Condition notFull;
    16. /**
    17. 迭代器
    18. */
    19. transient Itrs itrs = null;

    }

```

7.4.6 非阻塞式添加元素:add()、offer()方法的原理

7.4.7 阻塞式添加元素:put()方法的原理

7.4.8 非阻塞式删除元素:poll()方法的原理

7.4.9 阻塞式删除元素:take()方法的原理

7.4.10 peek()直接返回当前队列的头元素

7.5 ConcurrentHashMap

7.5.1 HashMap和HashTable的问题

7.5.2 JDK 1.7版本ConcurrentHashMap的结构

7.5.3 JDK 1.7版本ConcurrentHashMap的核心原理

7.5.4 JDK 1.8版本ConcurrentHashMap的结构

7.5.5 JDK 1.8版本ConcurrentHashMap的核心原理

7.5.6 JDK 1.8版本ConcurrentHashMap的核心源码