为什么juc下的集合类是线程安全的的

匿名 (未验证) 提交于 2019-12-02 23:49:02

也有一些线程安全的集合,如Vector,HashTable,但大多都是基于sychronized锁控制机制,性能很低。

如果既保证线程安全,执行效率又高,就可考虑下JUC下的集合类了,如:

ArrayList对应的高并发类是CopyOnWriteArrayList,

HashMap对应的高并发类是ConcurrentHashMap。

它们鱼与熊掌可兼得。但它们为什么在保证效率的同时还能保证线程安全呢?

那么我们先来看看CopyOnWriteArrayList。

CopyOnWriteArrayList提供高效地读取操作,使用在读多写少的场景。CopyOnWriteArrayList读取操作不用加锁,且是安全的;

写操作时,先copy一份原有数据数组,再对复制数据进行写入操作,最后将复制数据替换原有数据,从而保证写操作不影响读操作。

同时注意:其copy的整个过程是上了锁的,所以不会产生多线程安全问题。

我们来看看CopyOnWriteArrayList的核心的源码:

/**      * Appends the specified element to the end of this list.      *      * @param e element to be appended to this list      * @return {@code 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);//copy原有的数据数组             newElements[len] = e;  //对新的数据数组进行修改操作             setArray(newElements); //将修改后的数据替换原有的数据             return true;         } finally {             lock.unlock();         }     }

接下来来看看ConcurrentHashMap。

ConcurrentHashMap实现了HashTable的所有功能,线程安全,但却在检索元素时不需要锁定,因此效率更高。

是因为所有访问HashTable的线程都必须竞争同一把锁,那假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,

那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,

从而可以有效的提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术,

首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

这里来测试下ConcurrentHashMap:

import java.util.Hashtable; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap;  public class Test3 {      private static ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();      public static void main(String[] args) {         // 50 个线程执行         for (int i = 0; i < 50; i++) {             new MyThread().start();         }          try {             Thread.sleep(1000);         } catch (InterruptedException e) {             e.printStackTrace();         }          System.out.println("map集合的大小:" + map.size());//如果线程安全就是50X1000=500000     }      private static class MyThread extends Thread {         @Override         public void run() {             super.run();             for (int i = 0; i < 1000; i++) {                  map.put(UUID.randomUUID().toString(), i);              }          }     } }

测试结果如下:

map集合的大小:50000可以看出ConcurrentHashMap是线程安全的。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!