也有一些线程安全的集合,如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是线程安全的。