多线程六 同步容器&并发容器
2020-12-13 05:50
标签:state 依赖 ilo returns link each 入队 put 拷贝 本篇续 -- 线程之间的通信 ,介绍java提供的并发集合,既然正确的使用wait和notify比较困难,java平台为我们提供了更高级的并发容器来替代 将ArrayList转换成线程安全的 效率低下的HashTable,同Vector类似,它被HashMap替代掉了 HashTable底层是通过synchronized关键字来实现的,虽然线程安全,但是在高并发的情况下,效率却超级低,因为当一个线程访问HashTable的同步方法时,其他线程拿不到锁,就访问不了它的同步方法,比如线程1使用HashTable的put方法,其他线程不仅不能使用put,就连get()也被阻塞不可以存储null键值 线程不安全的HashMap 在高并发访问的情况下,不会使用HashMap,线程不安全,但是可以存储null键值 将HashMap转换成线程安全的 它是ArrayList 的一个线程安全的变体,其中所有可变操作(add、set 等等)都是通过对底层数组进行一次新的复制来实现的。 同样实现了List接口,那么其实它的使用和ArrayList完全一样的,只是内部的实现不一样 分析add方法 可以看到,其实类似读写分离, add()可能会出现线程安全问题,因此给它一把锁,get()不会出现线程安全性问题,因此没有锁,如果非要给get也加上锁,那么在add的时候,就不能get了,因为它拿不到锁对象 之所以说是读写分离,读read,读取的原数组,add写的时候,其实不是往原array里面写,而是分如下几步 分析remove方法 总结一下 jdk同样提供了一个高效同步Map,ConcurrentHashMap 他不允许空值 jdk1.7中ConcurrentHashMap内部实行了锁分离,分段式存储数据,然后每一段数据都会加上不同步的锁,所以当其中一条线程访问其中一段数据的时候,其他数据仍然可以被别的线程访问,同时,它的get()方法也是无锁的 jdk1.8中ConcurrentHashMap内部抛弃了锁分离而使用红黑树实现 在并发队列上,java提供了两套实现,一个是ConcurrentLinkedQueue的非阻塞型的队列,另一个是BlockingQueue接口,阻塞队列,同样他们继承了Queue接口 保证了在高并发的情况下,对link底层维护的链表的增删改各个节点的安全性 它通过无锁的方法,底层使用的是UNSAFE实现(保证线程的安全性) 出队 java.util.concurrent 主要应用场景: BlockingQueue是一个接口,因此我们学习ArrayBlockingQueue,它的底层维护着一个数组的阻塞队列 一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。队列的头部 是在队列中存在时间最长的元素。队列的尾部 是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。 此类支持对等待的生产者线程和使用者线程进行排序的可选公平策略。默认情况下,不保证是这种排序。然而,通过将公平性 (fairness) 设置为 true 而构造的队列允许按照 FIFO 顺序访问线程。公平性通常会降低吞吐量,但也减少了可变性和避免了“不平衡性”。 此类及其迭代器实现了 Collection 和 Iterator 接口的所有可选 方法。 既然叫阻塞队列,也就是说,他支持在多线程的条件下,多线程并发添加移除数组的元素,会被阻塞等待,而不会抛出空值异常或者报错,且数组的长度不可变 可以直接使用它,实现消费者生产者模式,他的底层是用Condition ReentrantLock实现的--,方法被lock()和unlock()锁住, 数组满就await , 出队后signal jdk1.6开始java提供的双端队列Deque(Double Ended Queue)允许在队列的头部或者尾部进行入队或者出队的操作. 使用链表实现了双端队列,因此它相对于ArrayDeque来说,没有了内存调整,数组复制的负担 无论是ListedList还是ArratDeque,都是线程不安全的 LinkedBlockingDeque 参考 博文 多线程六 同步容器&并发容器 标签:state 依赖 ilo returns link each 入队 put 拷贝 原文地址:https://www.cnblogs.com/ZhuChangwu/p/11150328.html同步容器(使用的是synchronized,并且不一定是百分百安全)
一. Vector&ArrayList
ArrayList al = new ArrayList();
Connections.syncchronizedList(al);
Map
HashMap map = new HashMap();
Map map1 = Collections.synchronizedMap(map);
并发容器J.U.C
一. 并发List--CopyOnWriteArrayList
基本的使用;
实现原理
/**
* 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; //获取锁ReentrantLock
lock.lock();
try {
Object[] elements = getArray(); // 获取CopyOnWriteArrayList内部维护的数组
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1); // 复制该数组到 newElements数组里面 并扩容
newElements[len] = e; //在数组最后添加新的元素
setArray(newElements); //让替换掉原来的数组
return true; //添加成功
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
return get(getArray(), index);
}
/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices). Returns the element that was removed from the list.
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
E oldValue = get(elements, index);
int numMoved = len - index - 1;
if (numMoved == 0) //移除最后一个
setArray(Arrays.copyOf(elements, len - 1));
else { // 移除其它的,当前元素后面的需要往前移动
Object[] newElements = new Object[len - 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
setArray(newElements);
}
return oldValue; // 移除谁,返回谁
} finally {
lock.unlock();
}
}
二. 并发Set
Set
三. 并发Map
Collections.synchronizedMap();
四. 并发Queue
1 . ConcurrentLinkQueue 非阻塞队列
实现原理
入队offer/**
* Inserts the specified element at the tail of this queue.
* As the queue is unbounded, this method will never return {@code false}.
*
* @return {@code true} (as specified by {@link Queue#offer})
* @throws NullPointerException if the specified element is null
*/
public boolean offer(E e) {
checkNotNull(e); // 检查新添加的内容是否为空
final Node
图解节点的添加过程,在001文件中
public E poll() {
restartFromHead:
for (;;) {
for (Node
阻塞队列BlockingQueue
接口 BlockingQueue
类型参数:
E - 在此 collection 中保持的元素类型
所有超级接口:
Collection
所有已知子接口:
BlockingDeque
所有已知实现类:
ArrayBlockingQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, PriorityBlockingQueue, SynchronousQueue
生产者和消费者模式
可阻塞方法
描述
put
将指定元素插入此队列中,将等待可用的空间(如果有必要)。
take
获取并移除此队列的头部,在元素变得可用之前一直等待(如果有必要)。
不发生阻塞
描述
add
将指定元素插入此队列中(如果立即可行且不会违反容量限制),成功时返回 true,如果当前没有可用的空间,则抛出 IllegalStateException
remove
从此队列中移除指定元素的单个实例(如果存在)。同样会抛异常
不发生阻塞
描述
offer
将指定元素插入此队列中(如果立即可行且不会违反容量限制),成功时返回 true,如果当前没有可用的空间,则返回 false。
poll
获取并移除此队列的头部,在指定的等待时间前等待可用的元素(如果有必要)。没有元素,返回null
五 .并发Deque
他是是实现类:
> 线程安全,但是它没有进行读写分离,也就说,同一时间,只允许一条线程对其操作,因此在并发中,它的性能,远远底于ConcurrentLinkQueue
你不就像风一样
文章标题:多线程六 同步容器&并发容器
文章链接:http://soscw.com/index.php/essay/31820.html