1、概念及使用介紹 html
在JAVA8的Map接口中,增長了一個方法computeIfAbsent,此方法簽名以下: java
public V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)
Map接口的實現類如HashMap,ConcurrentHashMap,HashTable等繼承了此方法,經過此方法能夠構建JAVA本地緩存,下降程序的計算量,程序的複雜度,使代碼簡潔,易懂。 緩存
此方法首先判斷緩存MAP中是否存在指定key的值,若是不存在,會自動調用mappingFunction(key)計算key的value,而後將key = value放入到緩存Map,java8會使用thread-safe的方式從cache中存取記錄。 安全
若是mappingFunction(key)返回的值爲null或拋出異常,則不會有記錄存入map 多線程
二 代碼樣例 併發
import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class Main { static Map<Integer, Integer> cache = new ConcurrentHashMap<>(); public static void main(String[] args) throws InterruptedException { cache.put(0, 0); cache.put(1, 1); // 普通方式 System.out.println("Fibonacci(7) = " + fibonacci(7)); // 採用java7的同步線程方式及java8的本地緩存的方式 System.out.println("FibonacciJava8(7) = " + fibonacciJava8(7)); System.out.println("FibonacciJava7(7) = " + fibonacciJava7(7)); // 構建多值Map樣例代碼 Map<String, HashSet<String>> map1 = new HashMap<>(); map1.computeIfAbsent("fruits", k -> genValue(k)).add("apple"); map1.computeIfAbsent("fruits", k -> genValue(k)).add("orange"); map1.computeIfAbsent("fruits", k -> genValue(k)).add("pear"); map1.computeIfAbsent("fruits", k -> genValue(k)).add("banana"); map1.computeIfAbsent("fruits", k -> genValue(k)).add("water"); System.out.println(map1); //測試多線程併發處理,是否同步操做 Map<String, String> map2 = new ConcurrentHashMap<>(); ExecutorService exec = Executors.newCachedThreadPool(); for (int i = 0; i < 5; i++) { exec.execute(() -> { map2.computeIfAbsent("name", k -> genValue2(k)); map2.computeIfAbsent("addr", k -> genValue2(k)); map2.computeIfAbsent("email", k -> genValue2(k)); map2.computeIfAbsent("mobile", k -> genValue2(k)); }); } exec.shutdown(); exec.awaitTermination(1, TimeUnit.SECONDS); System.out.println(map2); } static HashSet<String> genValue(String str) { return new HashSet<String>(); } static String genValue2(String str) { System.out.println("==="); return str + "2"; } /** * 普通的實現方式 普通方式使用大量的計算,存在性能問題. 而且計算量隨着n的增長呈指數級增長,須要用到一些緩存策略,而且是線程安全的. * * @param n * @return */ static int fibonacci(int n) { if (n == 0 || n == 1) return n; System.out.println("calculating Fibonacci(" + n + ")"); return fibonacci(n - 2) + fibonacci(n - 1); } /** * 採用java8的本地緩存方式 若是緩存MAP中不存在指定key的值,會自動調用mappingFunction(key)計算key的value * 而後將key = value放入到緩存Map,java8會使用thread-safe的方式從cache中存取記錄 * * @param n * @return */ static int fibonacciJava8(int n) { return cache.computeIfAbsent(n, (key) -> { System.out.println("calculating FibonacciJava8 " + n); return fibonacciJava8(n - 2) + fibonacciJava8(n - 1); }); } /** * 在java7中的實現方式 * 在java7中,經過synchronized進行線程同步,檢查緩存是否存在key對應的值,若是不存在才進行計算並放入緩存中 * 爲了更好的性能,須要使用 double-checked locking,那樣代碼會更復雜 * * @param n * @return */ static int fibonacciJava7(int n) { if (n == 0 || n == 1) return n; Integer result = cache.get(n); if (result == null) { synchronized (cache) { result = cache.get(n); if (result == null) { System.out.println("calculating FibonacciJava7(" + n + ")"); result = fibonacciJava7(n - 2) + fibonacciJava7(n - 1); cache.put(n, result); } } } return result; } }
三,程序運行結果 app
calculating Fibonacci(7) calculating Fibonacci(5) calculating Fibonacci(3) calculating Fibonacci(2) calculating Fibonacci(4) calculating Fibonacci(2) calculating Fibonacci(3) calculating Fibonacci(2) calculating Fibonacci(6) calculating Fibonacci(4) calculating Fibonacci(2) calculating Fibonacci(3) calculating Fibonacci(2) calculating Fibonacci(5) calculating Fibonacci(3) calculating Fibonacci(2) calculating Fibonacci(4) calculating Fibonacci(2) calculating Fibonacci(3) calculating Fibonacci(2) Fibonacci(7) = 13 calculating FibonacciJava8 7 calculating FibonacciJava8 5 calculating FibonacciJava8 3 calculating FibonacciJava8 2 calculating FibonacciJava8 4 calculating FibonacciJava8 6 FibonacciJava8(7) = 13 FibonacciJava7(7) = 13 {fruits=[orange, banana, apple, pear, water]} === === === === {name=name2, mobile=mobile2, addr=addr2, email=email2}
4、參考 性能
http://www.java8.org/caching-with-ConcurrentHashMap-in-java-8.html 測試
JDK8 API ui
http://stackoverflow.com/questions/19278443/how-do-i-use-the-new-computeifabsent-function