线程安全集合
默认情况下,各种 Collection 类型不是线程安全的。
然而,使集合线程安全是相当容易的。
List threadSafeList = Collections.synchronizedList(new ArrayList()); Set threadSafeSet = Collections.synchronizedSet(new HashSet()); Map threadSafeMap = Collections.synchronizedMap(new HashMap());
当你创建一个线程安全的集合时,你不应该通过原始集合访问它,只能通过线程安全包装器访问它。
从 Java 5 开始,java.util.collections 有几个新的线程安全集合,不需要各种 Collections.synchronized 方法。
List threadSafeList = new CopyOnWriteArrayList(); Set threadSafeSet = new ConcurrentHashSet(); Map threadSafeMap = new ConcurrentHashMap();
插入 ConcurrentHashMap
public class InsertIntoConcurrentHashMap { public static void main(String[] args) { ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap<>(); SomeObject value = new SomeObject(); Integer key = 1; SomeObject previousValue = concurrentHashMap.putIfAbsent(1, value); if (previousValue != null) { //Then some other value was mapped to key = 1. 'value' that was passed to //putIfAbsent method is NOT inserted, hence, any other thread which calls //concurrentHashMap.get(1) would NOT receive a reference to the 'value' //that your thread attempted to insert. Decide how you wish to handle //this situation. } else { //'value' reference is mapped to key = 1. } } }
并发集合
并发集合是线程安全集合的泛化,允许在并发环境中更广泛地使用。
虽然线程安全集合具有从多个线程中安全添加或者删除元素的功能,但它们不一定在同一上下文中具有安全迭代(一个线程可能无法安全地迭代一个线程中的集合,而另一个线程通过添加/删除元素)。
这是使用并发集合的地方。
由于迭代通常是集合中几种批量方法的基本实现,例如 addAll、removeAll 或者集合复制(通过构造函数或者其他方式)、排序……并发集合的用例实际上非常大。
例如,Java SE 5 java.util.concurrent.CopyOnWriteArrayList 是一个线程安全的并发 List 实现,它的 javadoc 声明:
“快照”样式的迭代器方法使用对创建迭代器时数组状态的引用。
这个数组在迭代器的生命周期内永远不会改变,所以干扰是不可能的,并且迭代器保证不会抛出 ConcurrentModificationException 。
因此,以下代码是安全的:
public class ThreadSafeAndConcurrent { public static final List LIST = new CopyOnWriteArrayList<>(); public static void main(String[] args) throws InterruptedException { Thread modifier = new Thread(new ModifierRunnable()); Thread iterator = new Thread(new IteratorRunnable()); modifier.start(); iterator.start(); modifier.join(); iterator.join(); } public static final class ModifierRunnable implements Runnable { @Override public void run() { try { for (int i = 0; i < 50000; i++) { LIST.add(i); } } catch (Exception e) { e.printStackTrace(); } } } public static final class IteratorRunnable implements Runnable { @Override public void run() { try { for (int i = 0; i < 10000; i++) { long total = 0; for(Integer inList : LIST) { total += inList; } System.out.println(total); } } catch (Exception e) { e.printStackTrace(); } } } }
另一个关于迭代的并发集合是 ConcurrentLinkedQueue:
迭代器是弱一致的,返回元素反映了在迭代器创建时或者之后的某个时刻的队列状态。
它们不会抛出 java.util.ConcurrentModificationException,并且可能与其他操作同时进行。
自迭代器创建以来队列中包含的元素将只返回一次。
并发集合是一个 允许多个线程同时访问的[集合]。
不同的线程通常可以遍历集合的内容并添加或者删除元素。
集合负责确保集合不会损坏。