如何用凡科建设手机教学网站,网站集约化建设的总体情况,粮油移动端网页设计素材,建筑方案设计怎么写一 JDK 提供的并发容器总结 JDK 提供的这些容器大部分在 java.util.concurrent 包中。 ConcurrentHashMap: 线程安全的 HashMap CopyOnWriteArrayList: 线程安全的 List#xff0c;在读多写少的场合性能非常好#xff0c;远远好于 Vector. ConcurrentLinkedQueue: 高效的并…一 JDK 提供的并发容器总结 JDK 提供的这些容器大部分在 java.util.concurrent 包中。 ConcurrentHashMap: 线程安全的 HashMap CopyOnWriteArrayList: 线程安全的 List在读多写少的场合性能非常好远远好于 Vector. ConcurrentLinkedQueue: 高效的并发队列使用链表实现。可以看做一个线程安全的 LinkedList这是一个非阻塞队列。 BlockingQueue: 这是一个接口JDK 内部通过链表、数组等方式实现了这个接口。表示阻塞队列非常适合用于作为数据共享的通道。 ConcurrentSkipListMap: 跳表的实现。这是一个 Map使用跳表的数据结构进行快速查找。 二 ConcurrentHashMap
这里救不多赘述了因为他比较常见
三 CopyOnWriteArrayList 3.1 CopyOnWriteArrayList 简介
public class CopyOnWriteArrayListE
extends Object
implements ListE, RandomAccess, Cloneable, Serializable
在很多应用场景中读操作可能会远远大于写操作。由于读操作根本不会修改原有的数据因此对于每次读取都进行加锁其实是一种资源浪费。我们应该允许多个线程同时访问 List 的内部数据毕竟读取操作是安全的。
这和ReentrantReadWriteLock 读写锁的思想非常类似也就是读读共享、写写互斥、读写互斥、写读互斥。JDK 中提供了 CopyOnWriteArrayList 类比相比于在读写锁的思想又更进一步。为了将读取的性能发挥到极致CopyOnWriteArrayList 读取是完全不用加锁的并且更厉害的是写入也不会阻塞读取操作。只有写入和写入之间需要进行同步等待。这样一来读操作的性能就会大幅度提升。那它是怎么做的呢 3.2 CopyOnWriteArrayList 是如何做到的 CopyOnWriteArrayList 类的所有可变操作addset 等等都是通过创建底层数组的新副本来实现的。当 List 需要被修改的时候我并不修改原有内容而是对原有数据进行一次复制将修改的内容写入副本。写完之后再将修改完的副本替换原来的数据这样就可以保证写操作不会影响读操作了。
从 CopyOnWriteArrayList 的名字就能看出CopyOnWriteArrayList 是满足CopyOnWrite 的 ArrayList所谓CopyOnWrite 也就是说在计算机如果你想要对一块内存进行修改时我们不在原有内存块中进行写操作而是将内存拷贝一份在新的内存中进行写操作写完之后呢就将指向原来内存指针指向新的内存原来的内存就可以被回收掉了。 3.3 CopyOnWriteArrayList 读取和写入源码简单分析 3.3.1 CopyOnWriteArrayList 读取操作的实现
读取操作没有任何同步控制和锁操作理由就是内部数组 array 不会发生修改只会被另外一个 array 替换因此可以保证数据安全。 /** The array, accessed only via getArray/setArray. */private transient volatile Object[] array;public E get(int index) {return get(getArray(), index);}SuppressWarnings(unchecked)private E get(Object[] a, int index) {return (E) a[index];}final Object[] getArray() {return array;}3.3.2 CopyOnWriteArrayList 写入操作的实现
CopyOnWriteArrayList 写入操作 add() 方法在添加集合的时候加了锁保证了同步避免了多线程写的时候会 copy 出多个副本出来。 /*** Appends the specified element to the end of this list.** param e element to be appended to this list* return {code true} (as specified by {link Collection#add})*/public boolean add(E e) {final ReentrantLock lock this.lock;lock.lock();//加锁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();//释放锁}}
四 ConcurrentLinkedQueue Java 提供的线程安全的 Queue 可以分为阻塞队列和非阻塞队列其中阻塞队列的典型例子是 BlockingQueue非阻塞队列的典型例子是 ConcurrentLinkedQueue在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列。 阻塞队列可以通过加锁来实现非阻塞队列可以通过 CAS 操作实现。
从名字可以看出ConcurrentLinkedQueue这个队列使用链表作为其数据结构ConcurrentLinkedQueue 应该算是在高并发环境中性能最好的队列了。它之所有能有很好的性能是因为其内部复杂的实现。
ConcurrentLinkedQueue 内部代码就不分析了大家知道 ConcurrentLinkedQueue 主要使用 CAS 非阻塞算法来实现线程安全就好了。
ConcurrentLinkedQueue 适合在对性能要求相对较高同时对队列的读写存在多个线程同时进行的场景即如果对队列加锁的成本较高则适合使用无锁的 ConcurrentLinkedQueue 来替代
五 BlockingQueue 上面我们己经提到了 ConcurrentLinkedQueue 作为高性能的非阻塞队列。下面我们要讲到的是阻塞队列——BlockingQueue。阻塞队列BlockingQueue被广泛使用在“生产者-消费者”问题中其原因是 BlockingQueue 提供了可阻塞的插入和移除的方法。当队列容器已满生产者线程会被阻塞直到队列未满当队列容器为空时消费者线程会被阻塞直至队列非空时为止。
BlockingQueue 是一个接口继承自 Queue所以其实现类也可以作为 Queue 的实现来使用而 Queue 又继承自 Collection 接口。下面是 BlockingQueue 的相关实现类 下面主要介绍一下:ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue这三个 BlockingQueue 的实现类。
5.2 ArrayBlockingQueue
5.1 BlockingQueue 简单介绍
ArrayBlockingQueue 是 BlockingQueue 接口的有界队列实现类底层采用数组来实现。ArrayBlockingQueue 一旦创建容量不能改变。其并发控制采用可重入锁来控制不管是插入操作还是读取操作都需要获取到锁才能进行操作。当队列容量满时尝试将元素放入队列将导致操作阻塞;尝试从一个空队列中取一个元素也会同样阻塞。
ArrayBlockingQueue 默认情况下不能保证线程访问队列的公平性所谓公平性是指严格按照线程等待的绝对时间顺序即最先等待的线程能够最先访问到 ArrayBlockingQueue。而非公平性则是指访问 ArrayBlockingQueue 的顺序不是遵守严格的时间顺序有可能存在当 ArrayBlockingQueue 可以被访问时长时间阻塞的线程依然无法访问到 ArrayBlockingQueue。如果保证公平性通常会降低吞吐量。如果需要获得公平性的 ArrayBlockingQueue可采用如下代码
private static ArrayBlockingQueueInteger blockingQueue new ArrayBlockingQueueInteger(10,true);
5.3 LinkedBlockingQueue
LinkedBlockingQueue 底层基于单向链表实现的阻塞队列可以当做无界队列也可以当做有界队列来使用同样满足 FIFO 的特性与 ArrayBlockingQueue 相比起来具有更高的吞吐量为了防止 LinkedBlockingQueue 容量迅速增损耗大量内存。通常在创建 LinkedBlockingQueue 对象时会指定其大小如果未指定容量等于 Integer.MAX_VALUE /***某种意义上的无界队列* Creates a {code LinkedBlockingQueue} with a capacity of* {link Integer#MAX_VALUE}.*/public LinkedBlockingQueue() {this(Integer.MAX_VALUE);}/***有界队列* Creates a {code LinkedBlockingQueue} with the given (fixed) capacity.** param capacity the capacity of this queue* throws IllegalArgumentException if {code capacity} is not greater* than zero*/public LinkedBlockingQueue(int capacity) {if (capacity 0) throw new IllegalArgumentException();this.capacity capacity;last head new NodeE(null);} 5.4 PriorityBlockingQueue
PriorityBlockingQueue 是一个支持优先级的无界阻塞队列。默认情况下元素采用自然顺序进行排序也可以通过自定义类实现 compareTo() 方法来指定元素排序规则或者初始化时通过构造器参数 Comparator 来指定排序规则。
PriorityBlockingQueue 并发控制采用的是 ReentrantLock队列为无界队列ArrayBlockingQueue 是有界队列LinkedBlockingQueue 也可以通过在构造函数中传入 capacity 指定队列最大的容量但是 PriorityBlockingQueue 只能指定初始的队列大小后面插入元素的时候如果空间不够的话会自动扩容。
简单地说它就是 PriorityQueue 的线程安全版本。不可以插入 null 值同时插入队列的对象必须是可比较大小的comparable否则报 ClassCastException 异常。它的插入操作 put 方法不会 block因为它是无界队列take 方法在队列为空的时候会阻塞。 六 ConcurrentSkipListMap
使用跳表实现 Map 和使用哈希算法实现 Map 的另外一个不同之处是哈希并不会保存元素的顺序而跳表内所有的元素都是排序的。因此在对跳表进行遍历时你会得到一个有序的结果。所以如果你的应用需要有序性那么跳表就是你不二的选择。JDK 中实现这一数据结构的类是 ConcurrentSkipListMap。
redis 的章节我已经说过调表这里就不多赘述了