0

從CopyOnWriteArrayList.java,add方法如下:爲什麼CopyOnWriteArrayList在寫入時複製?

public boolean add(E e) { 
    final ReentrantLock lock = this.lock; 
    lock.lock(); 
    try { 
     Object[] elements = getArray(); 
     int len = elements.length; 
     Object[] newElements = Arrays.copyOf(elements, len + 1); 
     newElements[len] = e; 
     setArray(newElements); 
     return true; 
    } finally { 
     lock.unlock(); 
    } 
    } 

不難明白,添加操作應該鎖定,是什麼讓我困惑的是,它的舊數據複製到新陣列,並放棄以前的一個。 同時得到方法如下:

public E get(int index) { 
     return (E)(getArray()[index]); 
    } 

get方法沒有鎖定。 我找到了一些解釋,有人說複製到一個新的數組可以避免add和get方法在同一個數組上運行。 我的問題是爲什麼兩個線程不能同時讀寫?

+3

http://stackoverflow.com/questions/17853112/in-what-situations-is-the-copyonwritearraylist-suitable/17853225#17853225 –

回答

2

如果你只是在類CopyOnWriteArrayList關於array參考參考的頂部你看問題的答案。

private volatile transient Object[] array; // this is volatile 

return (E)(getArray()[index]); 

返回的array[index]最新的副本,所以這是threadsafe

final Object[] getArray() { 
     return array; 
    } 

getArray就是array返回參考。

+0

我想我明白,THX。 另一件事,如果我定義了volatile int i = 1; 線程A讀取i,線程B i ++; 線程A會發生什麼樣的錯誤結果? –

+0

如果標記變量i爲'volatile'那麼不會有任何錯誤的結果揮發性確保我的最新副本 – amicngh

+1

注意標記參考揮發性不會使操作就可以了原子。對於'volatile int i = 1;','i ++'仍然是一個非原子的*讀 - 修改 - 寫*操作,並且從多個線程執行它可能會導致更新丟失。 – bowmore

0

實際上,寫入路徑鎖定的原因並不是因爲它需要考慮讀取路徑來提供線程安全性,而是因爲它想要序列化寫入器。由於寫入時複製技術取代了易失性引用,通常最好對該操作進行序列化。

這個想法的關鍵是通過複製現有值,修改它並替換參考來完成寫操作。由此也可以看出,一旦設置參考指向的對象總是只讀(即,沒有突變直接對參考引用的對象進行)。因此,讀者可以安全地訪問它,而無需同步。

讀寫可以同時發生。然而,其含義是讀取將看到即將失效的狀態,直到完成易失性參考集。

0

在得到()時,如果多個線程試圖從列表中獲得他們將沒有問題。 因爲由於易失性數組它總是會讀取最新的副本並從數組中返回元素。

過程中添加()或()設置每次他們創造了一個新的陣列,以避免相互執行的問題時,這是使物體線程安全,使一成不變的一種方式。

如果他們添加過程中使用相同的數組對象或設置,則他們必須做出遍歷synchronized.or可能拋出異常,如果任何線程添加/刪除對象遍歷

按期間列出java doc

java.util.ArrayList的線程安全變體,其中所有可變操作(add,set等)都是通過創建底層數組的新副本來實現的。

這通常是成本太高,但可能比替代品更有效,當遍歷操作遠多於突變,當你不能或不想進行同步遍歷

看到這個

package com.concurrent; 

import java.util.List; 
import java.util.concurrent.CopyOnWriteArrayList; 

public class CopyOnWriteArrayListTest { 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     CopyOnWriteArrayList<Integer> list=new CopyOnWriteArrayList<>(); 


     Viewer viewer=new Viewer(); 
     viewer.setList(list);  
     Thread t1=new Thread(viewer); 

     Adder adder=new Adder(); 
     adder.setList(list); 

     Thread t=new Thread(adder); 
     t.start(); 
     t1.start(); 

    } 

    static class Adder implements Runnable{ 

     private List<Integer> list; 
     public void setList(List<Integer> list) { 
      this.list = list; 
     } 
     @Override 
     public void run() { 
      for(int i=0;i<100;i++){ 
       list.add(i); 
       System.out.println("Added-"+i); 
       try { 
        Thread.sleep(500); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 

     } 

    } 

    static class Viewer implements Runnable{ 

     private List<Integer> list; 
     public void setList(List<Integer> list) { 
      this.list = list; 
     } 
     @Override 
     public void run() { 
      while (true) { 
       System.out.println("Length of list->"+list.size()); 
       for (Integer i : list) { 
        System.out.println("Reading-"+i); 
        try { 
         Thread.sleep(500); 
        } catch (InterruptedException e) { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
        } 
       } 
      } 

     } 

    } 
} 
是有用
相關問題