<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    莊周夢蝶

    生活、程序、未來
       :: 首頁 ::  ::  :: 聚合  :: 管理

    Clojure的transient集合

    Posted on 2010-08-18 18:46 dennis 閱讀(2284) 評論(0)  編輯  收藏 所屬分類: Clojure
        下班后記一些有趣的東西。

        這個話題是阿寶同學問我為什么clojure的PersistentVector的節點Node為什么要有個原子引用指向一個線程:
    static class Node implements Serializable {
        
    //記錄的線程
        transient final AtomicReference<Thread> edit;
        
    final Object[] array;

        Node(AtomicReference
    <Thread> edit, Object[] array){
            
    this.edit = edit;
            
    this.array = array;
        }

        Node(AtomicReference
    <Thread> edit){
            
    this.edit = edit;
            
    this.array = new Object[32];
        }
    }

        我還真不懂,沒有細看過這部分代碼,早上花點時間學習了下。
        PersistentVector的實現是另一個話題,這里不提。我們都知道clojure的數據結構是immutable的,修改任意一個數據結構都將生成一個新的數據結構,原來的不變。為了減少復制的開銷,clojure的數據結構同時是persistent,所謂持久數據結構是將數據組織為樹形的層次結構,修改的時候只是root改變,指向不同的節點,原有的節點仍然復用,從而避免了大量的數據復制,具體可以搜索下ideal hash trees這篇paper, paper難懂,可以看看這篇blog

        但是在創建PersistentVector的時候,從一堆現有的元素或者集合創建一個PersistentVector,如果每次都重新生成一個PersistentVector,未免太浪費,創建過程的性能會受到影響。我們完全可以假設創建PersistentVector這個過程肯定是線程安全的,沒有必要每添加一個元素就重新生成一個PersistentVector,完全可以在同一個PersistentVector上修改。這就是TransientVector的意義所在。
        TransientVector就是一個可修改的Vector,調用它添加一個元素,刪除一個元素,都是在同一個對象上進行,而不是生成新的對象。查看PersistentVector的創建:

    static public PersistentVector create(ISeq items){
        TransientVector ret 
    = EMPTY.asTransient();
        
    for(; items != null; items = items.next())
            ret 
    = ret.conj(items.first());
        
    return ret.persistent();
    }

    static public PersistentVector create(List items){
        TransientVector ret 
    = EMPTY.asTransient();
        
    for(Object item : items)
            ret 
    = ret.conj(item);
        
    return ret.persistent();
    }

    static public PersistentVector create(Object items){
        TransientVector ret 
    = EMPTY.asTransient();
        
    for(Object item : items)
            ret 
    = ret.conj(item);
        
    return ret.persistent();
    }


        看到三個方法的第一步都是將EMPTY集合transient化,生成一個可修改的TransientVector:
    TransientVector(PersistentVector v){
            
    this(v.cnt, v.shift, editableRoot(v.root), editableTail(v.tail));
        }

    static Node editableRoot(Node node){
            
    return new Node(new AtomicReference<Thread>(Thread.currentThread()), node.array.clone());
        }

        生成的時候記錄了當前的線程在root節點。然后添加元素的時候直接調用TransientVector的conj方法,查看conj可以看到每次返回的都是this:
    public TransientVector conj(Object val){
                    
    //確保修改過程合法
            ensureEditable();
             
    //忽略邏輯
            return this;
        }

        查看ensureEditable方法:
    void ensureEditable(){
            Thread owner 
    = root.edit.get();
            
    if(owner == Thread.currentThread())
                
    return;
            
    if(owner != null)
                
    throw new IllegalAccessError("Transient used by non-owner thread");
            
    throw new IllegalAccessError("Transient used after persistent! call");
        }


        終于看到Node中的edit引用的線程被使用了,判斷當前修改的線程是否是使得集合transient化的線程,如果不是則拋出異常,這是為了保證對TransientVector的編輯是在同一個線程里,防止因為意外發布TransientVector引用引起的線程安全隱患。

        知道了transient集合的用途,我們能在clojure中使用嗎?完全沒問題,clojure.core有個transient方法,可以將一個集合transient化:
    (defn transient 
      [
    ^clojure.lang.IEditableCollection coll] 
      (.asTransient coll))
      
        前提是這個集合是可編輯的,clojure的map、vector和set都是可編輯的。讓我們確認下transient修改后的集合還是不是自身:
    user=> (def v1 [1 2 3])
    #
    'user/v1
    user=> (def v2 (transient v1))
    #
    'user/v2
    user=> v2
    #
    <TransientVector clojure.lang.PersistentVector$TransientVector@7eb366>

        定義了集合v1,v2是調用了transient之后的集合,查看v2,果然是一個TransientVector。查看v2的元素個數是不是3個:
    user=> (.count v2)
    3

        沒問題,注意,我們不能直接調用count函數,因為v2是個普通的java對象,我們必須使用dot操作符來調用java對象的方法。添加一個元素看看:
    user=> (def v3 (.conj v2 4))
    #
    'user/v3
    user=> v3
    #
    <TransientVector clojure.lang.PersistentVector$TransientVector@7eb366>

        添加一個元素后形成集合v3,查看v3,跟v2是同一個對象#<TransientVector clojure.lang.PersistentVector$TransientVector@7eb366>
    。證明了transient集合修改的是自身,而不是生成一個新集合。確認下4有加入v2和v3:
    user=> (.nth v3 3)
    4
    user
    => (.count v2)
    4
    user
    => (.count v3)
    4
    user
    => (.nth v2 3)
    4

        果然沒有問題。transient集合的使用應當慎重,除非能確認沒有其他線程會去修改集合,并且對線程的可見性要求不高的時候,也許可以嘗試下這個技巧。
      
    主站蜘蛛池模板: 国内精品99亚洲免费高清| 亚洲免费在线观看| 亚洲AV无码一区二区二三区软件| 欧美a级成人网站免费| 中文字幕成人免费高清在线视频| 亚洲一区二区三区高清在线观看 | 成人免费视频一区| 97av免费视频| 亚洲视频在线免费播放| 亚洲电影免费在线观看| 51午夜精品免费视频| 深夜a级毛片免费视频| 青青久久精品国产免费看| 亚洲乱码无人区卡1卡2卡3| 亚洲高清一区二区三区| 中文字幕乱码亚洲无线三区 | 亚洲第一香蕉视频| 亚洲精品中文字幕乱码| 亚洲娇小性xxxx色| 亚洲欧洲日产国码久在线| 亚洲国产精品自在自线观看| 亚洲精品无AMM毛片| 亚洲AV无码专区亚洲AV桃| 爱情岛论坛免费视频| 国产午夜精品久久久久免费视| 两性色午夜视频免费播放| 99在线视频免费| 麻豆国产VA免费精品高清在线| 亚洲JIZZJIZZ中国少妇中文| 国产成人麻豆亚洲综合无码精品| 亚洲人成影院在线| 亚洲国产成人精品无码区花野真一| 四虎影视久久久免费| 18女人腿打开无遮掩免费| 啊v在线免费观看| 亚洲精品自在线拍| 国产在线观看无码免费视频| 色播精品免费小视频| 亚洲色欲一区二区三区在线观看| 亚洲免费在线观看视频| 久久国产精品免费看|