如果多线程要并发的修改一个数据结构,例如散列表,那么很容易破坏这个数据结构。可以通过锁来保护共享数据结构,但是选择线程安全的实现作为替代可能更容易些。
1.高效的映像、集合和队列
java.util.concurrent包提供了映像、集合和队列的高效实现:
(1)ConcurrentLinkedQueue : 一个基于链接节点的无界线程安全队列。此队列按照 FIFO(先进先出)原则对元素进行排序。队列的头部 是队列中时间最长的元素。队列的尾部 是队列中时间最短的元素。新的元素插入到队列的尾部,队列获取操作从队列头部获得元素。当多个线程共享访问一个公共 collection 时,ConcurrentLinkedQueue 是一个恰当的选择。此队列不允许使用 null 元素。
(2)ConcurrentHashMap : 支持获取的完全并发和更新的所期望可调整并发的哈希表。并发的散列镜像表默认支持16个写者线程同时执行,如果大于16个,其余的会被阻塞,但没有必要指定更大数目。
(3)ConcurrentSkipListMap : 可缩放的并发 ConcurrentNavigableMap 实现。映射可以根据键的自然顺序进行排序,也可以根据创建映射时所提供的 Comparator 进行排序,具体取决于使用的构造方法。
(4)ConcurrentSkipListSet : 一个基于 ConcurrentSkipListMap 的可缩放并发 NavigableSet 实现。set 的元素可以根据它们的自然顺序进行排序,也可以根据创建 set 时所提供的 Comparator 进行排序,具体取决于使用的构造方法。
这些集合使用复杂的算法,通过允许并发地访问数据结构的不同部分来使竞争极小化。
size方法 : 于大多数集合不同,size方法不必在常量时间内操作。确定这样的集合当前的大小通常需要遍历。
迭代器 : 集合返回弱一致性(weakly consisteut)的迭代器,这意味着迭代器不一定能反映出他们被构造之后的所有的修改。但是它们不会将同一个值返回两次,也不会抛出ConcurrentModificationException异常。与之形成对照的是,集合如果在迭代器构造之后发生改变,java.util包中得迭代器会抛出一个ConcurrentModificationException的异常。
ConcurrentHashMap和ConcurrentSkipListMap类 : 有相应的方法用于原子性的关联插入以及关联删除。
V putIfAbsent(K key, V value) : 该方法自动添加新的关联,前提是原来没有这一关联。对于多线程访问的缓存来说这很有用,确保只有一个线程向缓存添加项。
boolean remove(Object key, Object value) : putIfAbsent的相反操作,将原子性的删除键值对。只有目前将键的条目映射到给定值时,才移除该键的条目。
boolean replace(K key, V oldValue, V newValue) : 原子性的用新值替换旧值,假定旧值与指定的键值关联。只有目前将键的条目映射到给定值时,才替换该键的条目。
类 java.util.concurrent.ConcurrentLinkedQueue<E>
构造 ConcurrentLinkedQueue<E>() : 构造一个可以被多线程安全访问的无边界非阻塞的队列。
类 java.util.concurrent.ConcurrentHashMap<K,V>
构造 ConcurrentHashMap() : 创建一个带有默认初始容量 (16)、加载因子 (0.75) 和 concurrencyLevel (16) 的新的空映射。
ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) : 创建一个带有指定初始容量、加载因子和并发级别的新的空映射。
initialCapacity : 集合的初始容量,默认为16
loadFactor : 控制调整:如果每一个桶的平均负载超过这个因子,表的大小会被从新调整。默认值为0.75
concurrencyLevel : 并发写者线程的估计数目
类 java.util.concurrent.ConcurrentSkipListSet
构造 ConcurrentSkipListSet() : 构造一个新的空 set,该set按照元素的自然顺序对其进行排序。或元素实现comparable接口。
构造 ConcurrentSkipListSet(Comparator<? super E> comparator) : 构造一个新的空set,该set按照指定的比较器对其元素进行排序。
类 java.util.concurrent.ConcurrentSkipListMap<K,V>
构造 ConcurrentSkipListMap() : 构造一个新的空映射,该映射按照键的自然顺序进行排序。
构造 ConcurrentSkipListMap(Comparator<? super K> comparator) : 构造一个新的空映射,该映射按照指定的比较器进行排序。
2.写数组的拷贝
CopyOnWriteArrayList 和 CopyOnWriteArraySet是线程安全的集合。
分析构造
public CopyOnWriteArrayList(Collection<? extends E> c) {
Object[] elements = c.toArray();
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elements.getClass() != Object[].class)
elements = Arrays.copyOf(elements, elements.length, Object[].class);
setArray(elements);
}
public CopyOnWriteArraySet(Collection<? extends E> c) {
al = new CopyOnWriteArrayList<E>();
al.addAllAbsent(c);
}
得到结论:底层数组进行了复制。
3.旧的线程安全的集合
任何集合类通过使用同步包装器(sychronization wrapper)变成线程安全的
List<E> syncArrayList = Collections.synchronizedList(new ArrayList<E>);
Map<K, V) syncHashMap = Collections.synchronizedHashMap(new HashMap<K, V>);
结果集合的方法使用锁加以保护,提供了线程的安全访问
1.确保没有任何线程通过原始的非同步方法访问数据结构,最便利的方法是确保不保存任何指向原始对象的引用,构造一个集合并立即传递给包装器。
2.如果在另一个线程可能进行修改时要对集合进行迭代,仍然需要使用“客户端”封锁。
e.g.synchronized(syncHashMap){
Iterator<K> iter = syncHashMap.KeySet().iterator();
while(iter.hasNext()){
... ...
}
}
for-each必须使用同样地代码,如果在迭代过程中,别的线程修改集合,迭代器会失败,并抛出ConcurrentModificationException异常。
最好使用java.util.concurrent包中定义的集合,不使用同步包装器中得。有一个例外是经常被修改的列表,在那种情况下同步的ArrayList可以胜过CopyOnWriteArrayList。
分享到:
相关推荐
Java的多种多线程安全集合的介绍和示例代码
c#官方线程安全集合源码,concurrentBag concurrentqueue,concurrentset,concurrentDictionary,concurrentSet等
线程安全集合: Vector HashTable Properties 集合线程安全与解决方案 ArrayList线程安全问题 package com.raicho.mianshi.mycollection; import java.util.ArrayList; import java.util.List; import java....
.NET Framework4 线程安全集合详解
你还在用synchronized?线程安全相关知识深入剖析
Task task = BlockingCollectionUtil.AddTakeBlockingCollectionAsync(); Task.WaitAny(task); BlockingCollectionUtil.TryTakeBlockingCollection(); ConcurrentBagUtil.Test(); ConcurrentDictionaryUtil....
jdk中线程安全的集合类.docx
java集合类线程安全 写的不错,短小精悍,值得一读
hiredis的c++封装, 线程安全, 提供对键值对、队列、散列、集合结构的读写
主要介绍了详解java各种集合的线程安全,小编觉得挺不错的,这里分享给大家,供需要的朋友参考。
可以使用Java中的同步机制,如使用synchronized关键字或者使用线程安全的集合类来保证多线程操作的安全性。 实现步骤: 创建一个下载管理器类,用于管理下载任务和线程池。 在下载管理器中实现多线程下载的逻辑,...
高级程序员必会的HashMap的线程安全问题,适用于0~2年的
本资源包“Java多线程与线程安全实践-基于Http协议的断点续传.rar”是一个针对计算机科学专业学生设计的毕业设计或课程项目源码文件集合。该项目旨在通过实现一个基于HTTP协议的文件断点续传功能,深入探索和实践...
最近写了个小程序用到了C#4.0中的线程安全集合。想起很久以前用C#2.0开发的时候写后台windows服务,为了利用多线程实现生产者和消费者模型,经常要封装一些线程安全的容器,比如泛型队列和字典等等。下面就结合部分...
使用Collections.synchronizedList(new ArrayList()):内部直接将接受的List对象传递给静态内部类SynchronizedList对象,然后Collections.synchronizedList(new ArrayList())返回的List对象的调用方法都是直接调用...
go数据结构Go数据结构是有用的,高性能的和线程安全的Go数据结构的集合。 注意:仅在Go 1.3+上进行了测试。 增强树用于n维范围内go-datastructures中的碰撞的间隔树Go-datastructures是有用的,性能良好的和线程安全...
Java集合框架的线程安全.docx
线程安全的集合 线程安全的 List CopyOnWriteArrayList 线程安全的Set 线程安全的Map ConcurrentHashMap ConcurrentSkipListMap java集合 线程不安全的集合 HashMap的特点 HashMap在Jdk8之前使用拉链法实现,jdk8...
一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一...
介绍完整的,算是个开胃菜吧,类似CAS等更加底层的机制,后面会在Java进阶模块中的并发主题有更加系统的介绍。知识扩展1. 为什么需要 ConcurrentHa