【集合】List、Map、Set部分实现类是否线程安全

1、List

1.1ArrayList不是线程安全的。多线程情况下操作ArrayList 不是线性安全的,可能会抛出java.util.ConcurrentModificationException 并发修改异常。
1.2Vector是线程安全的。Vector对add和get方法都加了关键字synchronized修饰,所以是线程安全的,但是同一时间只能有一个线程访问进行读写,所以性能慢。

    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
    public synchronized E get(int index) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        return elementData(index);
    }

1.3Collections.synchronizedList(List<T> list) 通过Collections的synchronizedList静态方法,可以返归一个线程安全的SynchronizedList对象。SynchronizedList是Collections的静态内部类。

    public static <T> List<T> synchronizedList(List<T> list) {
        return (list instanceof RandomAccess ?
                new SynchronizedRandomAccessList<>(list) :
                new SynchronizedList<>(list));
    }

synchronizedLis 和 Vector的区别

  • 1 synchronizedList的实现中,synchronized关键字修饰代码块;Vector的实现中修饰方法。
  • 2 synchronizedList只封装了add、get、remove等代码块,但Iterator却不是同步的,进行遍历时要手动进行同步处理;Vector中对Iterator也进行了加锁。
  • 3 synchronizedList能够将所有List实现类封装为同步集合,其内部持有的仍然是List的实现类(ArrayList/LinkedList),所以除同步外,几乎只有该实现类和Vector的区别。
      public void add(int index, E element) {
            synchronized (mutex) {list.add(index, element);}
      }
      public E get(int index) {
            synchronized (mutex) {return list.get(index);}
      }

1.4CopyOnWriteArrayList<E> 写入时复制(CopyOnWrite)思想
数据存放在一个Object数组中,在进行add和set操作时会上锁,同时copy一份原始的Object数组,在完成add和set操作后,重新setArray,释放锁。get操作没有加锁,如果读的时候有多个线程正在向CopyOnWriteArrayList添加数据,不会阻塞,读还是可以读到旧的数据。

    private transient volatile Object[] array;

    public E get(int index) {
        return get(getArray(), index);
    }
    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();
        }
    }
    public E set(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            E oldValue = get(elements, index);

            if (oldValue != element) {
                int len = elements.length;
                Object[] newElements = Arrays.copyOf(elements, len);
                newElements[index] = element;
                setArray(newElements);
            } else {
                // Not quite a no-op; ensures volatile write semantics
                setArray(elements);
            }
            return oldValue;
        } finally {
            lock.unlock();
        }
    }

一个测试各种List是否安全的例子:

    public static void main(String[] args) {
        //List<String> list = new ArrayList<>();
        //List<String> list = new Vector<>();
        //List<String> list = Collections.synchronizedList(new ArrayList<>());
        List<String> list = new CopyOnWriteArrayList<>();

        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 7));
                System.out.println(list);
            }).start();
        }
    }

2、Map

2.1HashMap<>不是线程安全的。多线程情况下操作HashMap不是线性安全的,可能会抛出java.util.ConcurrentModificationException 并发修改异常。
  HashMap就有点复杂了,1.7由数组+链表实现,1.8由数组+链表+红黑树实现,不深入了,得单独学。
2.2Collections.synchronizedMap( new HashMap<>()) 通过Collections的synchronizedMap静态方法,可以返归一个线程安全的SynchronizedMap对象。SynchronizedMap是Collections的静态内部类。get和put都加锁

        public V get(Object key) {
            synchronized (mutex) {return m.get(key);}
        }

        public V put(K key, V value) {
            synchronized (mutex) {return m.put(key, value);}
        }

2.3class ConcurrentHashMap<K,V> 来自 java.util.concurrent.ConcurrentHashMap,支持检索的完全并发性和更新的高预期并发性的哈希表,单独学。
一个测试各种Map是否安全的例子:

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        //Map<String,String> map= Collections.synchronizedMap( new HashMap<>());
        //Map<String,String> map=new ConcurrentHashMap<>();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                map.put(UUID.randomUUID().toString().substring(0, 5), UUID.randomUUID().toString().substring(0, 5));
                System.out.println(map);
            }).start();
        }
    }

3、Set

3.1class HashSet<E>不是线程安全的。
  HashSetd底层由HashMap实现,HashSet的add方法调用对的是HashMap的put方法,传输的key是将传入set的元素,传入的value是一个固定的Object对象。

    private transient HashMap<E,Object> map;
    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

    public HashSet() {
        map = new HashMap<>();
    }
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }

3.2class TreeSet<E> 不是线程安全的。
3.3Collections.synchronizedSet(new HashSet<>()) 是线程安全的。
3.4class CopyOnWriteArraySet<E> 是线程安全的。
一个测试各种Set是否安全的例子:

    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        //Set<String > set =new TreeSet<>();
        //Set<String> set=Collections.synchronizedSet(new HashSet<>());
        //Set<String> set=new CopyOnWriteArraySet<>();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                set.add(UUID.randomUUID().toString().substring(0, 7));
                System.out.println(set);
            }).start();
        }
    }
部分继承关系图
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 集合类框架的介绍: ![Java 集合类框架](https://upload-images.jianshu.io/...
    LynnGuo阅读 4,101评论 0 1
  • java集合 为什么集合只能存放引用数据类型? java集合实际存放的是对象的引用,每个集合元素都是一个引用变量,...
    敲代码的小嵩鼠阅读 3,169评论 1 2
  • 1、概念简介: 线程安全:就是当多线程访问时,采用了加锁的机制;即当一个线程访问该类的某个数据时,会对这个数据进行...
    青见青阅读 12,464评论 0 4
  • 原文地址 Java集合 Java集合框架:是一种工具类,就像是一个容器可以存储任意数量的具有共同属性的对象。 Ja...
    gyl_coder阅读 4,560评论 0 8
  • 在编程中,常常需要集中存放多个数据。集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。所有的集合类都位于...
    一一一二二三阅读 3,229评论 0 1

友情链接更多精彩内容