咱們知道ArrayList和LinkedList實現的List都是非線程安全的,因而就有了Vector,它是基於ArrayList的線程安全集合,但Vector不管是add方法仍是get方法都加上了synchronized修飾,當多線程讀寫List必須排隊執行,很顯然這樣效率比較是低下的,那有沒有一種辦法讓效率提高,讓當讀List的時候線程是異步的,當寫List是同步的呢?答案是CopyOnWriteArrayList,他是讀寫分離的,好處是提升線程訪問效率,下面咱們對比下CopyOnWriteArrayList和Vector執行效率。java
import java.util.Vector; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; /** * @author :jiaolian * @date :Created in 2021-01-18 15:28 * @description:安全list性能對比 * @modified By: * 公衆號:叫練 */ public class SafeListTest { private static Vector<String> safeList = new Vector<>(); //private static CopyOnWriteArrayList<String> safeList = new CopyOnWriteArrayList<>(); private static CountDownLatch countDownLatch = new CountDownLatch(2); public static void main(String[] args) throws InterruptedException { //初始化 safeList.add("叫練"); MySerive fishSerive = new MySerive(); long start = System.currentTimeMillis(); new Thread(()->{ fishSerive.read(); countDownLatch.countDown(); },"叫練讀線程").start(); new Thread(()->{ fishSerive.write(); countDownLatch.countDown(); },"叫練寫線程").start(); countDownLatch.await(); System.out.println("花費:"+(System.currentTimeMillis()-start)); } private static class MySerive { //讀 public void read() { for (int i=0 ;i<1000000; i++) { safeList.get(0); } } //寫 public void write() { for (int i=0 ;i<100000; i++) { safeList.add("叫練"); } } } }
如上代碼:當安全集合用Vector時,執行時長是100毫秒,當安全集合用CopyOnWriteArrayList時,執行時長是5000毫秒,神碼?你不是說CopyOnWriteArrayList的效率要高麼?但執行狀況CopyOnWriteArrayList執行的時長居然是Vector的50倍!經過翻看源碼,咱們發現當CopyOnWriteArrayList寫元素時是經過備份數組的方式實現的,當多線程同步激烈,數據量較大時會不停的複製數組,內存浪費嚴重。這就是時過長的緣由!可是咱們仍是承認讀寫分離思想!數組
import java.util.Iterator; import java.util.Vector; import java.util.concurrent.CopyOnWriteArrayList; /** * @author :jiaolian * @date :Created in 2021-01-18 16:40 * @description:CopyOnWriteArrayList弱一致性 * @modified By: * 公衆號:叫練 */ public class WeekCopyOnWriteArrayListTest { private static CopyOnWriteArrayList<String> safeList = new CopyOnWriteArrayList<>(); //private static Vector<String> safeList = new Vector<>(); public static void main(String[] args) throws InterruptedException { safeList.add("叫"); safeList.add("練"); Iterator<String> iterator = safeList.iterator(); Thread thread = new Thread(()->{ //刪除下標爲0的元素 safeList.remove(0); }); thread.start(); //主線程等待thread執行完成; thread.join(); while (iterator.hasNext()) { System.out.println(iterator.next()); } } }
如上代碼:主線程等待thread子線程執行完畢,循環打印safeList元素,最終執行結果以下圖所示安全
你可能會有疑問,thread不是已經刪除「叫」嗎?控制檯不是應該只打印一個「練」字嗎?爲何還會打出「叫練」兩個字,緣由是main線程在執行Iterator<String> iterator = safeList.iterator();保存了元素快照,因此能看到這樣的執行結果,當thread線程執行完畢後,此時JVM內存狀態以下圖所示!多線程
提到fail-safe,會先提到fail-fast,字面上翻譯快速失敗,它是集合快速檢測失敗機制,防止集合不正確操做!通常狀況下,若是線程經過iterator方式循環集合時,另一個線程也修改了這個集合,咱們測試下,如上述測試弱一致性的代碼,將private static CopyOnWriteArrayList<String> safeList = new CopyOnWriteArrayList<>();換成private static Vector<String> safeList = new Vector<>();會發生什麼狀況呢?併發
如上圖,java.util.ConcurrentModificationException,集合併發修改錯誤,但換成CopyOnWriteArrayList執行正常,緣由是CopyOnWriteArrayList刪除數據時會有集合快照。異步
因此他是fail-safe,而Vector是fail-fast!ide
總結下吧,咱們用代碼簡述說明了CopyOnWriteArrayList的讀寫分離,弱一致性,fail-safe,fail-safe等概念,並簡述了實現原理。喜歡的請點贊加關注哦。我是叫練【公衆號】,邊叫邊練。性能