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

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

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

    xylz,imxylz

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

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

    常見(jiàn)的并發(fā)陷阱

    volatile

    volatile只能強(qiáng)調(diào)數(shù)據(jù)的可見(jiàn)性,并不能保證原子操作和線(xiàn)程安全,因此volatile不是萬(wàn)能的。參考指令重排序

    volatile最常見(jiàn)于下面兩種場(chǎng)景。

    a. 循環(huán)檢測(cè)機(jī)制

    volatile boolean done = false;


        while( ! done ){
            dosomething();
        }


    b. 單例模型 (http://www.tkk7.com/xylz/archive/2009/12/18/306622.html)

    public class DoubleLockSingleton {

        
    private static volatile DoubleLockSingleton instance = null;

        
    private DoubleLockSingleton() {
        }

        
    public static DoubleLockSingleton getInstance() {
            
    if (instance == null) {
                
    synchronized (DoubleLockSingleton.class) {
                    
    if (instance == null) {
                        instance 
    = new DoubleLockSingleton();
                    }
                }
            }
            
    return instance;
        }
    }

     


    synchronized/Lock

    看起來(lái)Lock有更好的性能以及更靈活的控制,是否完全可以替換synchronized?

    鎖的一些其它問(wèn)題中說(shuō)過(guò),synchronized的性能隨著JDK版本的升級(jí)會(huì)越來(lái)越高,而Lock優(yōu)化的空間受限于CPU的性能,很有限。另外JDK內(nèi)部的工具(線(xiàn)程轉(zhuǎn)儲(chǔ))對(duì)synchronized是有一些支持的(方便發(fā)現(xiàn)死鎖等),而對(duì)Lock是沒(méi)有任何支持的。

    也就說(shuō)簡(jiǎn)單的邏輯使用synchronized完全沒(méi)有問(wèn)題,隨著機(jī)器的性能的提高,這點(diǎn)開(kāi)銷(xiāo)是可以忽略的。而且從代碼結(jié)構(gòu)上講是更簡(jiǎn)單的。簡(jiǎn)單就是美。

    對(duì)于復(fù)雜的邏輯,如果涉及到讀寫(xiě)鎖、條件變量、更高的吞吐量以及更靈活、動(dòng)態(tài)的用法,那么就可以考慮使用Lock。當(dāng)然這里尤其需要注意Lock的正確用法。

    Lock lock = 
    lock.lock();
    try{
        //do something
    }finally{
        lock.unlock();
    }


    一定要將Lock的釋放放入finally塊中,否則一旦發(fā)生異常或者邏輯跳轉(zhuǎn),很有可能會(huì)導(dǎo)致鎖沒(méi)有釋放,從而發(fā)生死鎖。而且這種死鎖是難以排查的。

    如果需要synchronized無(wú)法做到的嘗試鎖機(jī)制,或者說(shuō)擔(dān)心發(fā)生死鎖無(wú)法自恢復(fù),那么使用tryLock()是一個(gè)比較明智的選擇的。

    Lock lock = 
    if(lock.tryLock()){
        try{
            //do something
        }finally{
            lock.unlock();
        }
    }

     

    甚至可以使用獲取鎖一段時(shí)間內(nèi)超時(shí)的機(jī)制Lock.tryLock(long,TimeUnit)。 鎖的使用可以參考前面文章的描述和建議。

    鎖的邊界

    一個(gè)流行的錯(cuò)誤是這樣的。

    ConcurrentMap<String,String> map = new ConcurrentHashMap<String,String>();

    if(!map.containsKey(key)){
        map.put(key,value);
    }


    看起來(lái)很合理的,對(duì)于一個(gè)線(xiàn)程安全的Map實(shí)現(xiàn),要存取一個(gè)不重復(fù)的結(jié)果,先檢測(cè)是否存在然后加入。 其實(shí)我們知道兩個(gè)原子操作和在一起的指令序列不代表就是線(xiàn)程安全的。 割裂的多個(gè)原子操作放在一起在多線(xiàn)程的情況下就有可能發(fā)生錯(cuò)誤。

    實(shí)際上ConcurrentMap提供了putIfAbsent(K, V)的“原子操作”機(jī)制,這等價(jià)于下面的邏輯:

    if(map.containsKey(key)){
        return map.get(key);
    }else{
        return map.put(k,v);
    }


    除了putIfAbsent還有replace(K, V)以及replace(K, V, V)兩種機(jī)制來(lái)完成組合的操作。

    提到Map,這里有一篇談HashMap讀寫(xiě)并發(fā)的問(wèn)題。

    構(gòu)造函數(shù)啟動(dòng)線(xiàn)程

    下面的實(shí)例是在構(gòu)造函數(shù)中啟動(dòng)一個(gè)線(xiàn)程。

    public class Runner{
       int x,y;
       Thread thread;
       public Runner(){
          this.x=1;
          this.y=2;
          this.thread=new MyThread();
          this.thread.start();
       }
    }


    這里可能存在的陷阱是如果此類(lèi)被繼承,那么啟動(dòng)的線(xiàn)程可能無(wú)法正確讀取子類(lèi)的初始化操作。

    因此一個(gè)簡(jiǎn)單的原則是,禁止在構(gòu)造函數(shù)中啟動(dòng)線(xiàn)程,可以考慮但是提供一個(gè)方法來(lái)啟動(dòng)線(xiàn)程。如果非要這么做,最好將類(lèi)設(shè)置為final,禁止繼承。

    丟失通知的問(wèn)題

    這篇文章里面提到過(guò)notify丟失通知的問(wèn)題。

    對(duì)于wait/notify/notifyAll以及await/singal/singalAll,如果不確定到底是否能夠正確的收到消息,擔(dān)心丟失通知,簡(jiǎn)單一點(diǎn)就是總是通知所有。

    如果擔(dān)心只收到一次消息,使用循環(huán)一直監(jiān)聽(tīng)是不錯(cuò)的選擇。

    非常主用性能的系統(tǒng),可能就需要區(qū)分到底是通知單個(gè)還是通知所有的掛起者。

    線(xiàn)程數(shù)

    并不是線(xiàn)程數(shù)越多越好,在下一篇文章里面會(huì)具體了解下性能和可伸縮性。 簡(jiǎn)單的說(shuō),線(xiàn)程數(shù)多少?zèng)]有一個(gè)固定的結(jié)論,受限于CPU的內(nèi)核數(shù),IO的性能以及依賴(lài)的服務(wù)等等。因此選擇一個(gè)合適的線(xiàn)程數(shù)有助于提高吞吐量。

    對(duì)于CPU密集型應(yīng)用,線(xiàn)程數(shù)和CPU的內(nèi)核數(shù)一致有助于提高吞吐量,所有CPU都很繁忙,效率就很高。 對(duì)于IO密集型應(yīng)用,線(xiàn)程數(shù)受限于IO的性能,某些時(shí)候單線(xiàn)程可能比多線(xiàn)程效率更高。但通常情況下適當(dāng)提高線(xiàn)程數(shù),有利于提高網(wǎng)絡(luò)IO的效率,因?yàn)槲覀兛偸钦J(rèn)為網(wǎng)絡(luò)IO的效率比較低。

    對(duì)于線(xiàn)程池而言,選擇合適的線(xiàn)程數(shù)以及任務(wù)隊(duì)列是提高線(xiàn)程池效率的手段。

    public ThreadPoolExecutor(
        int corePoolSize,
        int maximumPoolSize,
        long keepAliveTime,
        TimeUnit unit,
        BlockingQueue<Runnable> workQueue,
        ThreadFactory threadFactory,
        RejectedExecutionHandler handler)

     


    對(duì)于線(xiàn)程池來(lái)說(shuō),如果任務(wù)總是有積壓,那么可以適當(dāng)提高corePoolSize大小;如果機(jī)器負(fù)載較低,那么可以適當(dāng)提高maximumPoolSize的大小;任務(wù)隊(duì)列不長(zhǎng)的情況下減小keepAliveTime的時(shí)間有助于降低負(fù)載;另外任務(wù)隊(duì)列的長(zhǎng)度以及任務(wù)隊(duì)列的拒絕策略也會(huì)對(duì)任務(wù)的處理有一些影響。

     



    ©2009-2014 IMXYLZ |求賢若渴
    posted on 2011-12-30 17:25 imxylz 閱讀(6920) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): Java Concurrency

    ©2009-2014 IMXYLZ
    主站蜘蛛池模板: 在线精品自拍亚洲第一区| 亚洲Av无码精品色午夜| 亚洲天堂视频在线观看| 国产亚洲精品免费视频播放| 亚洲精品综合久久| 国产四虎免费精品视频| 亚洲视频免费在线播放| 足恋玩丝袜脚视频免费网站| 在线观看www日本免费网站| 成人免费激情视频| 毛片免费视频播放| 精品乱子伦一区二区三区高清免费播放 | 产传媒61国产免费| 一级做a爰全过程免费视频毛片| 一区二区三区视频免费| 国产精品偷伦视频免费观看了| 久久久受www免费人成| 日韩免费电影网址| 91高清免费国产自产| www.999精品视频观看免费| 福利免费在线观看| 久久成人免费播放网站| aⅴ在线免费观看| 黑人粗长大战亚洲女2021国产精品成人免费视频 | 黄色a级片免费看| 中国一级特黄高清免费的大片中国一级黄色片| 国产一区二区三区免费观在线 | 亚洲一区二区三区久久| 亚洲国产精品自在自线观看| 国产成人亚洲精品电影| 中国一级毛片免费看视频| 久9这里精品免费视频| 青苹果乐园免费高清在线| 又黄又爽的视频免费看| 亚洲国产精品无码中文字| 亚洲男人电影天堂| 亚洲无限乱码一二三四区| 亚洲无线电影官网| 亚洲免费综合色在线视频| 国产乱子伦精品免费视频| 亚洲一级毛片免费在线观看|