自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

CopyOnwrite 了解嗎?

開發(fā) 前端
CopyOnWrite 只是看字面意思就能看出來,就是在寫入時復(fù)制,說得輕巧,寫入時復(fù)制,具體是怎么實現(xiàn)的呢?

 [[338419]]

本文轉(zhuǎn)載自微信公眾號「Java極客技術(shù)」,作者鴨血粉絲 。轉(zhuǎn)載本文請聯(lián)系Java極客技術(shù)公眾號。  

概念

CopyOnWrite 只是看字面意思就能看出來,就是在寫入時復(fù)制,說得輕巧,寫入時復(fù)制,具體是怎么實現(xiàn)的呢?

先來說說思想,具體怎么實現(xiàn)等下分析

CopyOnWrite 的思想就是:當(dāng)向一個容器中添加元素的時候,不是直接在當(dāng)前這個容器里面添加的,而是復(fù)制出來一個新的容器,在新的容器里面添加元素,添加完畢之后再將原容器的引用指向新的容器,這樣就實現(xiàn)了寫入時復(fù)制

你還記得在提到數(shù)據(jù)庫的時候,一般都會說主從復(fù)制,讀寫分離嗎?CopyOnWrite 的設(shè)計思想是不是和經(jīng)常說的主從復(fù)制,讀寫分離如出一撤?

優(yōu)缺點

了解概念之后,對它的優(yōu)缺點應(yīng)該就比較好理解了

優(yōu)點就是,讀和寫可以并行執(zhí)行,因為讀的是原來的容器,寫的是新的容器,它們之間互不影響,所以讀和寫是可以并行執(zhí)行的,在某些高并發(fā)場景下,可以提高程序的響應(yīng)時間

但是呢,你也看到了, CopyOnWrite 是在寫入的時候,復(fù)制了一個新的容器出來,所以要考慮它的內(nèi)存開銷問題,又回到了在學(xué)算法時一直強調(diào)的一個思想:拿空間換時間

需要注意一下,它只保證數(shù)據(jù)的最終一致性。因為在讀的時候,讀取的內(nèi)容是原容器里面的內(nèi)容,新添加的內(nèi)容是讀取不到的

基于它的優(yōu)缺點應(yīng)該就可以得出一個結(jié)論:CopyOnWrite 適用于寫操作非常少的場景,而且還能夠容忍讀寫的暫時不一致 如果你的應(yīng)用場景不適合,那還是考慮使用別的方法來實現(xiàn)吧

還有一點需要注意的是:在寫入時,它會復(fù)制一個新的容器,所以如果有寫入需求的話,最好可以批量寫入,因為每次寫入的時候,容器都會進行復(fù)制,如果能夠減少寫入的次數(shù),就可以減少容器的復(fù)制次數(shù)

在 JUC 包下,實現(xiàn) CopyOnWrite 思想的就是 CopyOnWriteArrayList & CopyOnWriteArraySet 這兩個方法,本篇文章側(cè)重于講清楚 CopyOnWriteArrayList

CopyOnWriteArrayList

在 CopyOnWriteArrayList 中,需要注意的是 add 方法:

  1. public boolean add(E e) { 
  2.         final ReentrantLock lock = this.lock; 
  3.         // 在寫入的時候,需要加鎖,如果不加鎖的話,在多線程場景下可能會被 copy 出 n 個副本出來 
  4.         // 加鎖之后,就能保證在進行寫時,只有一個線程在操作 
  5.         lock.lock(); 
  6.         try { 
  7.             Object[] elements = getArray(); 
  8.             int len = elements.length; 
  9.             // 復(fù)制原來的數(shù)組 
  10.             Object[] newElements = Arrays.copyOf(elements, len + 1); 
  11.             // 將要添加的元素添加到新數(shù)組中 
  12.             newElements[len] = e; 
  13.             // 將對原數(shù)組的引用指向新的數(shù)組 
  14.             setArray(newElements); 
  15.             return true
  16.         } finally { 
  17.             lock.unlock(); 
  18.         } 
  19.     } 

在寫的時候需要加鎖,但是在讀取的時候不需要添加

因為讀取的是原數(shù)組的元素,對新數(shù)組沒有什么影響,加了鎖反而會增加性能開銷

  1. public E get(int index) { 
  2.  return get(getArray(), index); 

舉個例子:

  1. @Slf4j 
  2. public class ArrayListExample { 
  3.  
  4.     // 請求總數(shù) 
  5.     public static int clientTotal = 5000; 
  6.  
  7.     // 同時并發(fā)執(zhí)行的線程數(shù) 
  8.     public static int threadTotal = 200; 
  9.  
  10.     private static List<Integer> list = new ArrayList<>(); 
  11.  
  12.     public static void  main(String[] args) throws Exception{ 
  13.         ExecutorService executorService = Executors.newCachedThreadPool(); 
  14.         final Semaphore semaphore = new Semaphore(threadTotal); 
  15.         final CountDownLatch countDownLatch = new CountDownLatch(clientTotal); 
  16.         for (int i = 0; i < clientTotal; i++) { 
  17.             final int count = i; 
  18.             executorService.execute(()->{ 
  19.                 try { 
  20.                     semaphore.acquire(); 
  21.                     update(count); 
  22.                     semaphore.release(); 
  23.                 } catch (Exception e) { 
  24.                     log.error("exception",e); 
  25.                 } 
  26.                 countDownLatch.countDown(); 
  27.             }); 
  28.         } 
  29.         countDownLatch.await(); 
  30.         executorService.shutdown(); 
  31.         log.info("size:{}",list.size()); 
  32.     } 
  33.     private static void update(int i){ 
  34.         list.add(i); 
  35.     } 

上面是客戶端請求 5000 次,有 200 個線程在同時請求,我使用的是 ArrayList 實現(xiàn),咱們看下打印結(jié)果:

如果是線程安全的話,那么最后的結(jié)果應(yīng)該是 5000 才對,多運行幾次你會發(fā)現(xiàn),每次程序的執(zhí)行結(jié)果都是不一樣的

如果是 CopyOnWriteArrayList 呢?

  1. @Slf4j 
  2. public class CopyOnWriteArrayListExample { 
  3.  
  4.     // 請求總數(shù) 
  5.     public static int clientTotal = 5000; 
  6.  
  7.     // 同時并發(fā)執(zhí)行的線程數(shù) 
  8.     public static int threadTotal = 200; 
  9.  
  10.     private static List<Integer> list = new CopyOnWriteArrayList<>(); 
  11.  
  12.     public static void  main(String[] args) throws Exception{ 
  13.         ExecutorService executorService = Executors.newCachedThreadPool(); 
  14.         final Semaphore semaphore = new Semaphore(threadTotal); 
  15.         final CountDownLatch countDownLatch = new CountDownLatch(clientTotal); 
  16.         for (int i = 0; i < clientTotal; i++) { 
  17.             final int count = i; 
  18.             executorService.execute(()->{ 
  19.                 try { 
  20.                     semaphore.acquire(); 
  21.                     update(count); 
  22.                     semaphore.release(); 
  23.                 } catch (Exception e) { 
  24.                     log.error("excepiton",e); 
  25.                 } 
  26.                 countDownLatch.countDown(); 
  27.             }); 
  28.         } 
  29.         countDownLatch.await(); 
  30.         executorService.shutdown(); 
  31.         log.info("size:{}",list.size()); 
  32.     } 
  33.     private static void update(int i){ 
  34.         list.add(i); 
  35.     } 

多運行幾次,結(jié)果都是一樣的:

由此可見, CopyOnWriteArrayList 是線程安全的

 

責(zé)任編輯:武曉燕 來源: Java極客技術(shù)
相關(guān)推薦

2022-07-11 10:47:46

容器JAVA

2021-04-27 05:57:12

ReadWriteLo容器

2012-09-06 17:54:28

2022-07-26 00:00:22

HTAP系統(tǒng)數(shù)據(jù)庫

2020-01-15 10:17:41

Kubernetes容器負載均衡

2014-04-17 16:42:03

DevOps

2022-07-11 07:10:48

HTTP協(xié)議類型

2018-02-02 10:56:19

屏蔽機房擴建

2010-09-06 14:03:06

PPP身份認證

2021-01-15 07:44:21

SQL注入攻擊黑客

2021-11-09 09:48:13

Logging python模塊

2019-09-16 08:40:42

2021-01-12 12:07:34

Linux磁盤系統(tǒng)

2014-11-28 10:31:07

Hybrid APP

2020-02-27 10:49:26

HTTPS網(wǎng)絡(luò)協(xié)議TCP

2019-10-31 08:36:59

線程內(nèi)存操作系統(tǒng)

2023-03-16 10:49:55

2021-03-28 09:26:30

HttpHttp協(xié)議網(wǎng)絡(luò)協(xié)議

2017-10-18 22:01:12

2023-10-24 08:53:24

FutureTas并發(fā)編程
點贊
收藏

51CTO技術(shù)棧公眾號