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

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

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

    xylz,imxylz

    關注后端架構(gòu)、中間件、分布式和并發(fā)編程

       :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      111 隨筆 :: 10 文章 :: 2680 評論 :: 0 Trackbacks

    從這一節(jié)開始介紹鎖里面的最后一個工具:讀寫鎖(ReadWriteLock)。

    ReentrantLock 實現(xiàn)了標準的互斥操作,也就是一次只能有一個線程持有鎖,也即所謂獨占鎖的概念。前面的章節(jié)中一直在強調(diào)這個特點。顯然這個特點在一定程度上面減低了吞吐量,實際上獨占鎖是一種保守的鎖策略,在這種情況下任何“讀/讀”,“寫/讀”,“寫/寫”操作都不能同時發(fā)生。但是同樣需要強調(diào)的一個概念是,鎖是有一定的開銷的,當并發(fā)比較大的時候,鎖的開銷就比較客觀了。所以如果可能的話就盡量少用鎖,非要用鎖的話就嘗試看能否改造為讀寫鎖。

    ReadWriteLock描述的是:一個資源能夠被多個讀線程訪問,或者被一個寫線程訪問,但是不能同時存在讀寫線程。也就是說讀寫鎖使用的場合是一個共享資源被大量讀取操作,而只有少量的寫操作(修改數(shù)據(jù))。清單1描述了ReadWriteLock的API。

     清單1 ReadWriteLock 接口

    public interface ReadWriteLock {
        Lock readLock();
        Lock writeLock();
    }

    清單1描述的ReadWriteLock結(jié)構(gòu),這里需要說明的是ReadWriteLock并不是Lock的子接口,只不過ReadWriteLock借助Lock來實現(xiàn)讀寫兩個視角。在ReadWriteLock中每次讀取共享數(shù)據(jù)就需要讀取鎖,當需要修改共享數(shù)據(jù)時就需要寫入鎖。看起來好像是兩個鎖,但其實不盡然,在下一節(jié)中的分析中會解釋這點奧秘。

    在JDK 6里面ReadWriteLock的實現(xiàn)是ReentrantReadWriteLock。

    清單2 SimpleConcurrentMap

    package xylz.study.concurrency.lock;

    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.HashSet;
    import java.util.Map;
    import java.util.Set;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;

    public class SimpleConcurrentMap<K, V> implements Map<K, V> {

        final ReadWriteLock lock = new ReentrantReadWriteLock();

        final Lock r = lock.readLock();

        final Lock w = lock.writeLock();

        final Map<K, V> map;

        public SimpleConcurrentMap(Map<K, V> map) {
            this.map = map;
            if (map == null) throw new NullPointerException();
        }

        public void clear() {
            w.lock();
            try {
                map.clear();
            } finally {
                w.unlock();
            }
        }

        public boolean containsKey(Object key) {
            r.lock();
            try {
                return map.containsKey(key);
            } finally {
                r.unlock();
            }
        }

        public boolean containsValue(Object value) {
            r.lock();
            try {
                return map.containsValue(value);
            } finally {
                r.unlock();
            }
        }

        public Set<java.util.Map.Entry<K, V>> entrySet() {
            throw new UnsupportedOperationException();
        }

        public V get(Object key) {
            r.lock();
            try {
                return map.get(key);
            } finally {
                r.unlock();
            }
        }

        public boolean isEmpty() {
            r.lock();
            try {
                return map.isEmpty();
            } finally {
                r.unlock();
            }
        }

        public Set<K> keySet() {
            r.lock();
            try {
                return new HashSet<K>(map.keySet());
            } finally {
                r.unlock();
            }
        }

        public V put(K key, V value) {
            w.lock();
            try {
                return map.put(key, value);
            } finally {
                w.unlock();
            }
        }

        public void putAll(Map<? extends K, ? extends V> m) {
            w.lock();
            try {
                map.putAll(m);
            } finally {
                w.unlock();
            }
        }

        public V remove(Object key) {
            w.lock();
            try {
                return map.remove(key);
            } finally {
                w.unlock();
            }
        }

        public int size() {
            r.lock();
            try {
                return map.size();
            } finally {
                r.unlock();
            }
        }

        public Collection<V> values() {
            r.lock();
            try {
                return new ArrayList<V>(map.values());
            } finally {
                r.unlock();
            }
        }

    }

    清單2描述的是用讀寫鎖實現(xiàn)的一個線程安全的Map。其中需要特別說明的是并沒有實現(xiàn)entrySet()方法,這是因為實現(xiàn)這個方法比較復雜,在后面章節(jié)中講到ConcurrentHashMap的時候會具體談這些細節(jié)。另外這里keySet()和values()也沒有直接返回Map的視圖,而是一個映射原有元素的新視圖,其實這個entrySet()一樣,是為了保護原始Map的數(shù)據(jù)邏輯,防止不正確的修改導致原始Map發(fā)生數(shù)據(jù)錯誤。特別說明的是在沒有特別需求的情況下沒有必要按照清單2寫一個線程安全的Map實現(xiàn),因為ConcurrentHashMap已經(jīng)完成了此操作。

     

    ReadWriteLock需要嚴格區(qū)分讀寫操作,如果讀操作使用了寫入鎖,那么降低讀操作的吞吐量,如果寫操作使用了讀取鎖,那么就可能發(fā)生數(shù)據(jù)錯誤。

    另外ReentrantReadWriteLock還有以下幾個特性:

    • 公平性
      • 非公平鎖(默認) 這個和獨占鎖的非公平性一樣,由于讀線程之間沒有鎖競爭,所以讀操作沒有公平性和非公平性,寫操作時,由于寫操作可能立即獲取到鎖,所以會推遲一個或多個讀操作或者寫操作。因此非公平鎖的吞吐量要高于公平鎖。
      • 公平鎖 利用AQS的CLH隊列,釋放當前保持的鎖(讀鎖或者寫鎖)時,優(yōu)先為等待時間最長的那個寫線程分配寫入鎖,當前前提是寫線程的等待時間要比所有讀線程的等待時間要長。同樣一個線程持有寫入鎖或者有一個寫線程已經(jīng)在等待了,那么試圖獲取公平鎖的(非重入)所有線程(包括讀寫線程)都將被阻塞,直到最先的寫線程釋放鎖。如果讀線程的等待時間比寫線程的等待時間還有長,那么一旦上一個寫線程釋放鎖,這一組讀線程將獲取鎖。
    • 重入性
      • 讀寫鎖允許讀線程和寫線程按照請求鎖的順序重新獲取讀取鎖或者寫入鎖。當然了只有寫線程釋放了鎖,讀線程才能獲取重入鎖。
      • 寫線程獲取寫入鎖后可以再次獲取讀取鎖,但是讀線程獲取讀取鎖后卻不能獲取寫入鎖。
      • 另外讀寫鎖最多支持65535個遞歸寫入鎖和65535個遞歸讀取鎖。
    • 鎖降級
      • 寫線程獲取寫入鎖后可以獲取讀取鎖,然后釋放寫入鎖,這樣就從寫入鎖變成了讀取鎖,從而實現(xiàn)鎖降級的特性。
    • 鎖升級
      • 讀取鎖是不能直接升級為寫入鎖的。因為獲取一個寫入鎖需要釋放所有讀取鎖,所以如果有兩個讀取鎖視圖獲取寫入鎖而都不釋放讀取鎖時就會發(fā)生死鎖。
    • 鎖獲取中斷
      • 讀取鎖和寫入鎖都支持獲取鎖期間被中斷。這個和獨占鎖一致。
    • 條件變量
      • 寫入鎖提供了條件變量(Condition)的支持,這個和獨占鎖一致,但是讀取鎖卻不允許獲取條件變量,將得到一個UnsupportedOperationException異常。
    • 重入數(shù)
      • 讀取鎖和寫入鎖的數(shù)量最大分別只能是65535(包括重入數(shù))。這在下節(jié)中有介紹。

    上面幾個特性對讀寫鎖的理解很有幫助,而且也是必要的,另外在下一節(jié)中講ReadWriteLock的實現(xiàn)會用到這些知識的。

     

     



    ©2009-2014 IMXYLZ |求賢若渴
    posted on 2010-07-14 14:18 imxylz 閱讀(24252) 評論(4)  編輯  收藏 所屬分類: J2EE

    評論

    # re: 深入淺出 Java Concurrency (13): 鎖機制 part 8 讀寫鎖 (ReentrantReadWriteLock) (1)[未登錄] 2010-07-14 20:11 行云流水
    默默支持。。  回復  更多評論
      

    # re: 深入淺出 Java Concurrency (13): 鎖機制 part 8 讀寫鎖 (ReentrantReadWriteLock) (1) 2012-09-05 14:11 我們
    但是我測試有問題,當同一個線程下一個先獲得讀鎖,就不能再進行寫鎖。因為涉及到公共方法的嵌套調(diào)用,會出現(xiàn)這種情況,而且任何一個鎖都不能取消.對待新問題要仔細研究,利用前人經(jīng)驗避免一些損失.
    監(jiān)測盜用可以用隨機算法和自己特有的特性來標示跟蹤,但是小隨機數(shù)也會產(chǎn)生測試很難遇到小可能情況從而使得隱患逃避測試  回復  更多評論
      

    # re: 深入淺出 Java Concurrency (13): 鎖機制 part 8 讀寫鎖 (ReentrantReadWriteLock) (1) 2014-04-02 10:56 laobailong
    你那個map居然是final的,final也可以更改數(shù)據(jù)嗎?  回復  更多評論
      

    # re: 深入淺出 Java Concurrency (13): 鎖機制 part 8 讀寫鎖 (ReentrantReadWriteLock) (1) 2014-04-02 11:28 imxylz
    @laobailong
    建議你學習下final的語法和用途  回復  更多評論
      


    ©2009-2014 IMXYLZ
    主站蜘蛛池模板: 亚洲视频免费在线观看| 亚洲中文无码永久免费 | 亚洲AV无码成人精品区在线观看| 久久精品国产亚洲Aⅴ香蕉| 久久精品国产亚洲精品| 在线a亚洲v天堂网2019无码| 亚洲精品无码永久中文字幕| 亚洲国产精品无码一线岛国| 亚洲成AV人在线播放无码| 亚洲v高清理论电影| 亚洲精品免费视频| 亚洲理论片在线中文字幕| 91亚洲国产成人久久精品网址| 亚洲人成网男女大片在线播放| 亚洲欧洲免费无码| 特黄特色大片免费| 三级网站在线免费观看| 99re6在线精品视频免费播放| 亚洲三级在线免费观看| 成人a视频片在线观看免费| 免费国产精品视频| 亚洲人成网亚洲欧洲无码久久| 亚洲国产日韩在线视频| 亚洲免费视频在线观看| 亚洲乱码一二三四区麻豆| 亚洲高清国产拍精品熟女| 欧亚一级毛片免费看| 国产午夜精品理论片免费观看| 免费A级毛片无码A∨| 永久免费毛片在线播放| 免费二级毛片免费完整视频| 亚洲人成网77777色在线播放| 久久久亚洲裙底偷窥综合| 亚洲国产成人手机在线观看| 一区二区三区免费精品视频| 免费A级毛片无码A∨| 国产精品深夜福利免费观看| 在线亚洲午夜理论AV大片| 亚洲一区二区三区精品视频| 黄色网址大全免费| 99视频在线精品免费|