线程安全的set_Javaset集合的线程安全类你知道吗?ConcurrentHashSet了解一下

从文章标题看,你可能很疑惑JDK中没有ConcurrentHashSet这个类啊,说对了,这篇文章就是分享怎样使用 ConcurrentHashMap 来试下 ConcurrentHashSet。

实现ConcurrentHashSet之前,我们先来回顾一下Set接口有哪些子类?我们先看下类图:

d04444a9817d2318d7f211c5c6deba40.png

从上面看,set的子类有四个,其中 :HaseSet、TreeSet、LinkedHashSet.这三个都是线程不安全的,CopyOnWriteArraySet是线程安全的。

即使CopyOnWriteArraySet 是线程安全的,它也不适合大型线程安全集合的应用程序,它仅用于设置大小保持较小且读大于写的应用程序,那么为什么呢?我们从源码上进行分析

CopyOnWriteArraySet 内部是使用 CopyOnWriteArrayList 来实现的,那么我们分析 CopyOnWriteArrayList的添加元素方法。

/**     * Appends the specified element to the end of this list.     *     * @param e element to be appended to this list     * @return 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();    }    }

上面每次添加元素的时候都需要创建一个新容器,然后把旧容器的元素copy进去,最终把新容器的赋值给旧容器,整个的操作是在锁当中调用的,所以是线程安全的。

那么为什么不建议在并发高的应用程序中使用呢?有以下两点

(1) 性能问题:

每次修改都创建一个新数组,然后复制所有内容,如果数组比较大,修改操作又比较频繁,可以想象,性能是很低的,而且内存开销会很大。

(2) 数据一致性问题。

CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。所以如果你希望写入的的数据,马上能读到,不要使用CopyOnWrite容器。

ConcurrentHashSet 的创建

可能有的同学会将,我也可以直接使用 ConcurrentHashMap 来进行操作,没必要使用ConcurrentHashSet。事实上,这正是 ConcurrentHashSet的 实现原理,底层是用 ConcurrentHashMap来实现的。

实现起来还是很简单的,因为HashSet 的底层也是使用HashMap来实现的。

public class ConcurrentHashSet extends AbstractSet implements Set, java.io.Serializable {    private static final long serialVersionUID = -8672117787651310382L;    private static final Object PRESENT = new Object();    private final ConcurrentHashMap map;    public ConcurrentHashSet() {        map = new ConcurrentHashMap();    }    public ConcurrentHashSet(int initialCapacity) {        map = new ConcurrentHashMap(initialCapacity);    }    /**     * Returns an iterator over the elements in this set. The elements are     * returned in no particular order.     *     * @return an Iterator over the elements in this set     * @see ConcurrentModificationException     */    public Iterator iterator() {        return map.keySet().iterator();    }    /**     * Returns the number of elements in this set (its cardinality).     *     * @return the number of elements in this set (its cardinality)     */    public int size() {        return map.size();    }    /**     * Returns true if this set contains no elements.     *     * @return true if this set contains no elements     */    public boolean isEmpty() {        return map.isEmpty();    }    /**     * Returns true if this set contains the specified element. More     * formally, returns true if and only if this set contains an     * element e such that     * (o==null ? e==null : o.equals(e)).     *     * @param o element whose presence in this set is to be tested     * @return true if this set contains the specified element     */    public boolean contains(Object o) {        return map.containsKey(o);    }    /**     * Adds the specified element to this set if it is not already present. More     * formally, adds the specified element e to this set if this set     * contains no element e2 such that     * (e==null ? e2==null : e.equals(e2)). If this     * set already contains the element, the call leaves the set unchanged and     * returns false.     *     * @param e element to be added to this set     * @return true if this set did not already contain the specified     * element     */    public boolean add(E e) {        return map.put(e, PRESENT) == null;    }    /**     * Removes the specified element from this set if it is present. More     * formally, removes an element e such that     * (o==null ? e==null : o.equals(e)), if this     * set contains such an element. Returns true if this set contained     * the element (or equivalently, if this set changed as a result of the     * call). (This set will not contain the element once the call returns.)     *     * @param o object to be removed from this set, if present     * @return true if the set contained the specified element     */    public boolean remove(Object o) {        return map.remove(o) == PRESENT;    }    /**     * Removes all of the elements from this set. The set will be empty after     * this call returns.     */    public void clear() {        map.clear();    }}

ConcurrentHashSet继承了AbstractSet,实现了Set、Serializable接口;它底层使用ConcurrentHashMap来实现,其value固定为PRESENT。

其他线程安全的Set 有哪些?

1、java1.6已经帮我们实现了

Set acceptedClassLoaders = Collections.newSetFromMap(new ConcurrentHashMap(16));

2、Collections 的synchronizedSet方法

static  Set synchronizedSet(Set s, Object mutex) {    return new SynchronizedSet<>(s, mutex);}

3、使用 CopyOnWriteArraySet

133588e82fdd75f65c4c0426f10d569c.png

4、谷歌的guava其实已经实现了线程安全的ConcurrentHashSet

Set s = Sets.newConcurrentHashSet();public static  Set newConcurrentHashSet() {    return newSetFromMap(new ConcurrentHashMap());}static  Set newSetFromMap(Map map) {    return Collections.newSetFromMap(map);}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值