集合类不安全之Set

1、不安全的Set

上代码:

    public static void main(String[] args) throws Exception {

        Set<String> set = new HashSet<>();

        for (int i = 0;i<30;i++){
            new Thread(()->{
                set.add(Thread.currentThread().getName());
                System.out.println(Thread.currentThread().getName()+"	"+set);
            }).start();
        }
    }

//运行结果如下:多线程共同修改set集合,造成了并发修改异常
java.util.ConcurrentModificationException
	at java.util.HashMap$HashIterator.nextNode(HashMap.java:1445)
	at java.util.HashMap$KeyIterator.next(HashMap.java:1469)
	at java.util.AbstractCollection.toString(AbstractCollection.java:461)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at juc.ContainerNotSafeDemo.lambda$main$0(ContainerNotSafeDemo.java:17)
	at java.lang.Thread.run(Thread.java:748)

2、安全的解决方式

使用CopyOnWriteArraySet解决

    public static void main(String[] args) throws Exception {

        Set<String> set = new CopyOnWriteArraySet<>();//new HashSet<>();

        for (int i = 0;i<30;i++){
            new Thread(()->{
                set.add(Thread.currentThread().getName());
                System.out.println(Thread.currentThread().getName()+"	"+set);
            }).start();
        }
    }

关于写时复制技术在这篇博客里写过,不再赘述。深入探索一下,看源码:

public class CopyOnWriteArraySet<E> extends AbstractSet<E>   
        implements java.io.Serializable {
    private static final long serialVersionUID = 5457747651344034263L;

    private final CopyOnWriteArrayList<E> al;

    /**
     * Creates an empty set.
     */
    public CopyOnWriteArraySet() {            //无参构造方法
        al = new CopyOnWriteArrayList<E>();  //实际上创建的是CopyOnWriteArrayList
    }
    ....
}

3、关于HashSet的补充

HashSet底层是什么?看源码:

    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }

注释的意思是:创建一个空的HashMap,初始容量是16,负载因子是0.75.

HashSet在add的时候传的是一个值并不是<k,v>,比如下边:

new HashSet<>().add("hello");

确定HashSet底层是HashMap吗???

这是一个容易被忽略的地方,查看HashSet的add方法源码:

    /**
     * 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
     * 如果该集合已经包含元素,则调用将不修改集合,返回false。
     * @param e element to be added to this set
     * @return <tt>true</tt> if this set did not already contain the specified
     * element
     */
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

可以看到,add方法其实是给HashMap添加了Key,value是PRESENT,它是一个Object类型的常量。调用的还是map的add方法。



原文地址:https://www.cnblogs.com/simon-1024/p/12096198.html