🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # 1. 线程安全的集合/映射/队列 下面列出的集合/映射/队列都是线程安全的。 * java.util.concurrent.ConcurrentLinkedQueue< E> 5.0 * ConcurrentLinkedQueue< E>() 构造一个可以被多线程安全访问的无边界非阻塞的队列。 * java.utilc.concurrent.ConcurrentSkipListSet< E> 6 * ConcurrentSkipListSet< E>() * ConcurrentSkipListSet< E>(Comparator<? super E> comp 构造一个可以被多线程安全访问的有序集。第一个构造器要求元素实现Comparable接口。 * java.util.concurrent.ConcurrentHashMap< K, V> 5.0 * ConcurrentHashMap< K, V>() * ConcurrentHashMap< K, V>(int initialCapacity) * ConcurrentHashMap< K, V>(int initialCapacity, float loadFactor, int concurrecyLevel) 构造一个可以被多线程安全访问的散列映射表,不允许存储null。 `initialCapacity`:集合的初始容量。默认为16。 `loadFactor`:控制调整,如果每一个桶的平均负载超过这个因子,表的大小将被重新调整。默认值0.75。 `concurrencyLevel`:并发写者程序的数目。 * java.util.concurrent.ConcurrentSkipListMap< K, V> 6 * ConcurrrentSkipListMap< K, V>() * ConcurrentSkipListMap< K, V>(Comparator< ? super K> comp) 构造一个可以被多线程安全访问的有序的映射表。第一个构造器要求键实现Comparable接口。 <br/> # 2. 映射表的更新 更新:将一个映射表的键对应的旧值,更新为新值。 <br/> **1. ConcurrentHashMap的更新** ```java public class ConcurrentHashMapTest { /** * 调用replace方法,与do/while循环组合,它会以原子方式用一个新值替换原值, * 如果替换成功,返回true。前提是之前没有其它线程把原值替换为其它值。 */ private void replaceTest() { ConcurrentHashMap<String, Long> map = new ConcurrentHashMap<>(1); map.put("one", 100L); //100 System.out.println(map.get("one")); Long oldValue = new Long(0L); Long newValue = new Long(0L); do { oldValue = map.get("one"); newValue = oldValue == null ? 1L : oldValue + 100L; } while (!map.replace("one", oldValue, newValue)); //直到更新成功,才退出while循环 //100 System.out.println(map.get("one")); } /** * 调用putIfAbsent方法更新。没有原值才更新,否则不更新。 */ private void putIfAbsentTest() { ConcurrentHashMap<String, Long> map = new ConcurrentHashMap<>(1); map.put("one", 1L); //1 System.out.println(map.get("one")); //null System.out.println(map.get("two")); map.putIfAbsent("one", 2L); map.putIfAbsent("two", 2L); //1 System.out.println(map.get("one")); //2 System.out.println(map.get("two")); } /** * 调用compute方法更新 */ private void computeTest() { ConcurrentHashMap<String, Long> map = new ConcurrentHashMap<>(1); map.put("one", 1L); //1 System.out.println(map.get("one")); map.compute("one", (k, v) -> v == null ? 0L : v + 1L); //2 System.out.println(map.get("one")); } /** * 调用merge方法更新。 */ private void mergeTest() { ConcurrentHashMap<String, Long> map = new ConcurrentHashMap<>(1); map.put("one", 1L); //1 System.out.println(map.get("one")); //null System.out.println(map.get("two")); //如果键不存在,则用1L为初始值 //如果键存在,则将使用提供的方法来结合原值和初始值 //sum方法,它会将初始值与原值进行和操作 map.merge("one", 1L, Long::sum); map.merge("two", 1L, Long::sum); //2 System.out.println(map.get("one")); //1 System.out.println(map.get("two")); } /** * 调用computeIfPresent方法更新,有原值才更新,否则不更新。 */ private void computeIfPresentTest() { ConcurrentHashMap<String, Long> map = new ConcurrentHashMap<>(1); map.put("one", 1L); //1 System.out.println(map.get("one")); //null System.out.println(map.get("two")); map.computeIfPresent("one", (k, v) -> v + 1); map.computeIfPresent("two", (k, v) -> v + 1); //2 System.out.println(map.get("one")); //null System.out.println(map.get("two")); } /** * 调用computeIfAbsent方法更新,没有原值才更新,否则不更新。 */ private void computeIfAbsentTest() { ConcurrentHashMap<String, Long> map = new ConcurrentHashMap<>(1); map.put("one", 1L); //1 System.out.println(map.get("one")); //null System.out.println(map.get("two")); map.computeIfAbsent("one", k -> 999L); map.computeIfAbsent("two", k -> 1L); //1 System.out.println(map.get("one")); //1 System.out.println(map.get("two")); } public static void main(String[] args) { ConcurrentHashMapTest test = new ConcurrentHashMapTest(); test.replaceTest(); //test.putIfAbsentTest(); //test.computeTest(); //test.mergeTest(); //test.computeIfPresentTest(); //test.computeIfAbsentTest(); } } ``` <br/> # 3. 映射表的批操作 总共有三类不同的批操作,每类批操作有4种不同的批处理方式。 ![](https://img.kancloud.cn/a6/8a/a68a3dc6f6522dde35cc4e6ef26b7560_935x334.png) * 搜索:为每个键,或值提供一个函数。 * 归约:组合所有的键,或值。 * forEach:为所有的键、或值提供一个函数。 映射表的批操作方法都有一个 threshold 阈值,如果映射表的元素多于这个阈值,则并行完成批操作;如果阈值为`Long.MAX_VALUE`,则只允许一个线程操作;如果阈值为`1`,则允许许多线程操作。 <br/> 所有的批操作方法都需要自定义一个函数,然后批操作方法就会自动调用你定义的方法去处理映射表。 <br/> **1. 调用`search`方法判断映射表的第一个值是否大于100** ```java @Test public void searchTest() { ConcurrentHashMap<Long, Long> map = new ConcurrentHashMap<>(); map.put(new Long(1), 2000L); map.put(new Long(2), 3000L); map.put(new Long(3), 1000L); map.put(new Long(4), 1000L); //search的第一个参数为阈值 boolean result = map.search(1, (k, v) -> { //k=1, v=2000 System.out.println("k=" + k + ", v=" + v); return v <= 1000 ? true : false; }); //false System.out.println(result); } ``` **2. 使用`forEach`方法遍历映射表** ```java /** * forEach遍历 */ @Test public void forEachTest() { ConcurrentHashMap<Long, Long> map = new ConcurrentHashMap<>(1); map.put(new Long(1), 2000L); map.put(new Long(2), 3000L); map.put(new Long(3), 1000L); map.put(new Long(4), 1000L); map.forEach(1, (k, v) -> System.out.println("Key=" + k + " Value=" + v)); //Key=1 Value=2000 //Key=3 Value=1000 //Key=2 Value=3000 //Key=4 Value=1000 } /** * forEach遍历,并过滤 */ @Test public void forEachTest2() { ConcurrentHashMap<Long, Long> map = new ConcurrentHashMap<>(1); map.put(new Long(1), 2000L); map.put(new Long(2), 3000L); map.put(new Long(3), 1000L); map.put(new Long(4), 1000L); //只要大于 1000 的元素 map.forEach(1, (k, v) -> v > 1000 ? "Key=" + k + " Value=" + v : null, System.out::println); //Key=1 Value=2000 //Key=2 Value=3000 } ``` **3. 调用`reduceValues`函数来求映射表中值的总和** ```java @Test public void reduceValues() { ConcurrentHashMap<Long, Long> map = new ConcurrentHashMap<>(1); map.put(new Long(1), 2000L); map.put(new Long(2), 3000L); map.put(new Long(3), 1000L); map.put(new Long(4), 1000L); Long sum = map.reduceValues(1, Long::sum); //7000 System.out.println(sum); } ```