`

(七) 线程安全的集合

阅读更多

如果多线程要并发的修改一个数据结构,例如散列表,那么很容易破坏这个数据结构。可以通过锁来保护共享数据结构,但是选择线程安全的实现作为替代可能更容易些。
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多线程安全集合

    在Java编程中,多线程安全集合是程序员在并发环境下处理数据共享时必须考虑的关键概念。这些集合确保了在多个线程访问时的数据一致性、完整性和安全性,避免了竞态条件、死锁和其他并发问题。Java提供了一系列的线程...

    c#官方线程安全集合源码

    在.NET框架中,C#提供了一系列的线程安全集合类,这些类被设计为在多线程环境下可以安全地进行数据操作。这些类是通过`System.Collections.Concurrent`命名空间提供的,确保了在并发访问时的数据一致性。让我们深入...

    Java集合多线程安全.docx

    然而,并非所有集合类都是线程安全的,这在多线程环境中可能导致数据不一致或者运行时异常。线程安全指的是一个对象在多线程环境下可以被正确地访问和修改,而不会出现竞态条件或死锁等问题。 线程不安全的集合类...

    java集合类线程安全.doc

    Java 集合类线程安全 Java 集合框架是由 Java 平台标准版 1.2 引入的通用数据结构与算法框架。其灵活的面对对象设计受到了广大 Java 程序员的一致青睐,为 Java 平台的成熟奠定了坚实的基础。 线程安全不是一个...

    C#多线程List的非线程安全性

    2. 使用线程安全的集合:.NET框架提供了线程安全的集合类,如ConcurrentBag、ConcurrentQueue和ConcurrentStack等,它们在内部实现了线程同步,可以安全地在多线程环境下使用。 3. 避免在多线程环境中直接修改List...

    .NET Framework4 线程安全集合详解

    - **概述**:BlockingCollection&lt;T&gt; 是一个高级的线程安全集合,实现了 `IProducerConsumerCollection&lt;T&gt;` 接口。它提供了线程间的生产者-消费者模式,支持添加和移除操作的阻塞,可以限制集合的大小,避免资源过度...

    jdk中线程安全的集合类.docx

    综上所述,`ConcurrentHashMap`和`ConcurrentSkipListMap`都是为了解决多线程环境下的并发问题而设计的高效线程安全集合类。`ConcurrentHashMap`通过分段锁和细粒度锁的机制提高了并发性能,而`...

    关于如何解决HashMap线程安全问题的介绍

    但是需要注意,虽然这个方法可以保证基本的线程安全,但迭代仍然是非线程安全的,即不能在遍历过程中修改Map。 2. 使用ConcurrentHashMap:Java从1.5版本开始引入了ConcurrentHashMap,它是线程安全且高并发性能的...

    Java的线程安全与不安全集合.docx

    线程安全集合: 1. `Vector`: 类似于`ArrayList`,但它在每个操作上都添加了`synchronized`关键字,使其成为线程安全的。然而,由于它的同步机制过于简单,性能通常较低。 2. `HashTable`: 类似于`HashMap`,但...

    C# 多线程资料集合

    本资源集合提供了一套全面的C#多线程学习资料,包括编程实例实战、线程技术详解以及多线程相关概念的阐述,特别适合于想要深入理解和掌握C#线程编程的开发者。 首先,C#中的线程允许程序员同时执行多个任务,这被...

    C#线程例子集合

    - **线程安全与并发**:在多线程环境中使用ADO.NET时,必须注意线程安全问题。例如,数据库连接不应在多个线程间共享,而是每个线程应有自己的连接。 - **线程间的通信**:在面试中可能会问到如何在不同线程间传递...

    ArrayList的线程安全测试

    在C#中,如果要在多线程中使用ArrayList,必须采取适当的同步措施,例如使用`lock`关键字、Monitor类或者`System.Collections.Concurrent`命名空间中的线程安全集合。 首先,让我们了解ArrayList的基本操作。...

    Java多线程运算集合

    - 原子类提供了线程安全的整型、长整型等基本类型操作,避免了使用锁带来的性能开销。 #### 十七、Java线程:新特征-障碍器 - **障碍器**: - `CyclicBarrier` 和 `CountDownLatch` 是两种常用的屏障类,用于...

    构建线程安全应用程序

    在构建线程安全应用程序时,除了使用同步机制和ThreadLocal变量,还可以使用并发集合和高级并发对象。比如,Java的ConcurrentHashMap是一个线程安全的哈希表实现,它通过分段锁机制来实现线程安全,并提供较高的并发...

    java线程安全总结.doc

    2. 使用线程安全的集合类,如`ConcurrentHashMap`、`CopyOnWriteArrayList`等。 3. 避免长时间持有锁,减少阻塞时间。 4. 优先使用更高粒度的同步,减少锁的使用范围。 5. 对于不可变对象,尽可能地使用它们,减少...

    详解java各种集合的线程安全

    Java 集合的线程安全详解 Java 集合的线程安全是指在多线程环境中,集合操作的安全性。 Java 中有多种集合类,如 Vector、ArrayList、LinkedList、HashTable、HashMap、HashSet 等,这些集合类在多线程环境中的安全...

    hiredis的c++封装, 线程安全

    本文将深入探讨如何使用C++进行hiredis的封装,以实现线程安全的Redis客户端操作。 首先,hiredis是Redis官方提供的一个纯C语言的简单协议解析库,它专注于处理Redis命令和响应,而忽略了更高级别的抽象。为了在C++...

    Go-golang-set-Go的线程安全的和非线程安全的高性能集

    `golang-set`库提供了一种实现,包括线程安全和非线程安全的高性能集,非常适合在Go的并发环境中使用。 首先,我们要理解什么是线程安全和非线程安全。线程安全指的是在多线程环境下,一个函数或方法在同一时刻可以...

    第10讲 如何保证集合是线程安全的 ConcurrentHashMap如何实现高效地线程安全1

    首先,Java提供了一种简单粗暴的方法,即使用synchronized关键字或者Collections.synchronizedMap()方法来使非线程安全的集合变得线程安全。但是,这种全局锁的机制在高并发环境下可能导致性能瓶颈,因为所有操作...

Global site tag (gtag.js) - Google Analytics