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

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

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

    kxbin
    成功留給有準備的人
    posts - 10,  comments - 35,  trackbacks - 0

    基于線程安全的一些原則來編程當然可以避免并發(fā)問題,但不是所有人都能寫出高質量的線程安全的代碼,并且如果代碼里到處都是線程安全的控制也極大地影響了代碼可讀性和可維護性。因此,Java平臺為了解決這個問題,提供了很多線程安全的類和并發(fā)工具,通過這些類和工具就能更簡便地寫線程安全的代碼。歸納一下有以下幾種:

    • 同步容器類
    • 并發(fā)容器類
    • 生產(chǎn)者和消費者模式
    • 阻塞和可中斷方法
    • Synchronizer

    這些類和方法的使用都可以從JDK DOC查到,但在具體使用中還是有很多問題需要注意

    同步容器類

    同步容器類就是一些經(jīng)過同步處理了的容器類,比如List有Vector,Map有Hashtable,查看其源碼發(fā)現(xiàn)其保證線程安全的方式就是把每個對外暴露的存取方法用synchronized關鍵字同步化,這樣做我們立馬會想到有以下問題:
    1)性能有問題

    同步化了所有存取方法,就表明所有對這個容器對象的操作將會串行,這樣做來得倒是干凈,但性能的代價也是很可觀的

    2)復合操作問題

    同步容器類只是同步了單一操作,如果客戶端是一組復合操作,它就沒法同步了,依然需要客戶端做額外同步,比如以下代碼:

     

    1. public static Object getLast(Vector list) {  
    2.     int lastIndex = list.size() - 1;  
    3.     return list.get(lastIndex);  
    4. }  
    5. public static void deleteLast(Vector list) {  
    6.     int lastIndex = list.size() - 1;  
    7.     list.remove(lastIndex);  
    8. }  

     

    getLast和deleteLast都是復合操作,由先前對原子性的分析可以判斷,這依然存在線程安全問題,有可能會拋出ArrayIndexOutOfBoundsException的異常,錯誤產(chǎn)生的邏輯如下所示:

     

    解決辦法就是通過對這些復合操作加鎖

    3)迭代器并發(fā)問題

    Java Collection進行迭代的標準時使用Iterator,無論是使用老的方式迭代循環(huán),還是Java 5提供for-each新方式,都需要對迭代的整個過程加鎖,不然就會有Concurrentmodificationexception異常拋出。

    此外有些迭代也是隱含的,比如容器類的toString方法,或containsAll, removeAll, retainAll等方法都會隱含地對容器進行迭代

    并發(fā)容器類

    正是由于同步容器類有以上問題,導致這些類成了雞肋,于是Java 5推出了并發(fā)容器類,Map對應的有ConcurrentHashMap,List對應的有CopyOnWriteArrayList。與同步容器類相比,它有以下特性:

    • 更加細化的鎖機制。同步容器直接把容器對象做為鎖,這樣就把所有操作串行化,其實這是沒必要的,過于悲觀,而并發(fā)容器采用更細粒度的鎖機制,保證一些不會發(fā)生并發(fā)問題的操作進行并行執(zhí)行
    • 附加了一些原子性的復合操作。比如putIfAbsent方法
    • 迭代器的弱一致性。它在迭代過程中不再拋出Concurrentmodificationexception異常,而是弱一致性。在并發(fā)高的情況下,有可能size和isEmpty方法不準確,但真正在并發(fā)環(huán)境下這些方法也沒什么作用。
    • CopyOnWriteArrayList采用寫入時復制的方式避開并發(fā)問題。這其實是通過冗余和不可變性來解決并發(fā)問題,在性能上會有比較大的代價,但如果寫入的操作遠遠小于迭代和讀操作,那么性能就差別不大了

    生產(chǎn)者和消費者模式

    大學時學習操作系統(tǒng)多會為生產(chǎn)者和消費者模式而頭痛,也是每次考試肯定會涉及到的,而Java知道大家很憷這個模式的并發(fā)復雜性,于是乎提供了阻塞隊列(BlockingQueue)來滿足這個模式的需求。阻塞隊列說起來很簡單,就是當隊滿的時候寫線程會等待,直到隊列不滿的時候;當隊空的時候讀線程會等待,直到隊不空的時候。實現(xiàn)這種模式的方法很多,其區(qū)別也就在于誰的消耗更低和等待的策略更優(yōu)。以LinkedBlockingQueue的具體實現(xiàn)為例,它的put源碼如下:

     

    1. public void put(E e) throws InterruptedException {  
    2.     if (e == nullthrow new NullPointerException();  
    3.     int c = -1;  
    4.     final ReentrantLock putLock = this.putLock;  
    5.     final AtomicInteger count = this.count;  
    6.     putLock.lockInterruptibly();  
    7.     try {  
    8.         try {  
    9.             while (count.get() == capacity)  
    10.                 notFull.await();  
    11.         } catch (InterruptedException ie) {  
    12.             notFull.signal(); // propagate to a non-interrupted thread  
    13.             throw ie;  
    14.         }  
    15.         insert(e);  
    16.         c = count.getAndIncrement();  
    17.         if (c + 1 < capacity)  
    18.             notFull.signal();  
    19.     } finally {  
    20.         putLock.unlock();  
    21.     }  
    22.     if (c == 0)  
    23.         signalNotEmpty();  
    24. }  

     

    撇開其鎖的具體實現(xiàn),其流程就是我們在操作系統(tǒng)課上學習到的標準生產(chǎn)者模式,看來那些枯燥的理論還是有用武之地的。其中,最核心的還是Java的鎖實現(xiàn),有興趣的朋友可以再進一步深究一下

    阻塞和可中斷方法

    由LinkedBlockingQueue的put方法可知,它是通過線程的阻塞和中斷阻塞來實現(xiàn)等待的。當調用一個會拋出InterruptedException的方法時,就成為了一個阻塞的方法,要為響應中斷做好準備。處理中斷可有以下方法:

    • 傳遞InterruptedException。把捕獲的InterruptedException再往上拋,使其調用者感知到,當然在拋之前需要完成你自己應該做的清理工作,LinkedBlockingQueue的put方法就是采取這種方式
    • 中斷其線程。在不能拋出異常的情況下,可以直接調用Thread.interrupt()將其中斷。

    Synchronizer

    Synchronizer不是一個類,而是一種滿足一個種規(guī)則的類的統(tǒng)稱。它有以下特性:

    • 它是一個對象
    • 封裝狀態(tài),而這些狀態(tài)決定著線程執(zhí)行到某一點是通過還是被迫等待
    • 提供操作狀態(tài)的方法

    其實BlockingQueue就是一種Synchronizer。Java還提供了其他幾種Synchronizer

    1)CountDownLatch

    CountDownLatch是一種閉鎖,它通過內(nèi)部一個計數(shù)器count來標示狀態(tài),當count>0時,所有調用其await方法的線程都需等待,當通過其countDown方法將count降為0時所有等待的線程將會被喚起。使用實例如下所示:

     

    1. public class TestHarness {  
    2.     public long timeTasks(int nThreads, final Runnable task)  
    3.             throws InterruptedException {  
    4.         final CountDownLatch startGate = new CountDownLatch(1);  
    5.         final CountDownLatch endGate = new CountDownLatch(nThreads);  
    6.         for (int i = 0; i < nThreads; i++) {  
    7.             Thread t = new Thread() {  
    8.                 public void run() {  
    9.                     try {  
    10.                         startGate.await();  
    11.                         try {  
    12.                             task.run();  
    13.                         } finally {  
    14.                             endGate.countDown();  
    15.                         }  
    16.                     } catch (InterruptedException ignored) { }  
    17.                 }  
    18.             };  
    19.             t.start();  
    20.         }  
    21.         long start = System.nanoTime();  
    22.         startGate.countDown();  
    23.         endGate.await();  
    24.         long end = System.nanoTime();  
    25.         return end-start;  
    26.     }  
    27. }  

     

    2)Semaphore

    Semaphore類實際上就是操作系統(tǒng)中談到的信號量的一種實現(xiàn),其原理就不再累述,可見探索并發(fā)編程------操作系統(tǒng)篇

    具體使用就是通過其acquire和release方法來完成,如以下示例:

     

    1. public class BoundedHashSet<T> {  
    2.     private final Set<T> set;  
    3.     private final Semaphore sem;  
    4.     public BoundedHashSet(int bound) {  
    5.         this.set = Collections.synchronizedSet(new HashSet<T>());  
    6.         sem = new Semaphore(bound);  
    7.     }  
    8.     public boolean add(T o) throws InterruptedException {  
    9.         sem.acquire();  
    10.         boolean wasAdded = false;  
    11.         try {  
    12.             wasAdded = set.add(o);  
    13.             return wasAdded;  
    14.         }  
    15.         finally {  
    16.             if (!wasAdded)  
    17.                 sem.release();  
    18.         }  
    19.     }  
    20.     public boolean remove(Object o) {  
    21.         boolean wasRemoved = set.remove(o);  
    22.         if (wasRemoved)  
    23.             sem.release();  
    24.         return wasRemoved;  
    25.     }  
    26. }  

     

    3)關卡

    關卡和閉鎖類似,也是阻塞一組線程,直到某件事情發(fā)生,而不同在于關卡是等到符合某種條件的所有線程都達到關卡點。具體使用上可以用CyclicBarrier來應用關卡

     

    以上是Java提供的一些并發(fā)工具,既然是工具就有它所適用的場景,因此需要知道它的特性,這樣才能在具體場景下選擇最合適的工具。

    posted on 2011-10-13 16:02 kxbin 閱讀(282) 評論(0)  編輯  收藏 所屬分類: java基礎
    你恨一個人是因為你愛他;你喜歡一個人,是因為他身上有你沒有的;你討厭一個人是因為他身上有你有的東西;你經(jīng)常在別人面前批評某人,其實潛意識中是想接近他。

    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    常用鏈接

    留言簿(5)

    隨筆檔案

    文章分類

    文章檔案

    相冊

    收藏夾

    J2EE

    java技術網(wǎng)站

    Linux

    平時常去的網(wǎng)站

    數(shù)據(jù)庫

    電影網(wǎng)站

    網(wǎng)站設計

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲日本在线观看| 亚洲第一福利网站在线观看| 91麻豆精品国产自产在线观看亚洲| 亚洲成在人线在线播放无码| 成人黄色免费网址| 久久亚洲精品无码VA大香大香| 日韩a级无码免费视频| 亚洲精品无码乱码成人| 国产自国产自愉自愉免费24区| 亚洲另类激情综合偷自拍图| 99麻豆久久久国产精品免费 | 亚洲人成网站在线观看青青| 在线精品自拍亚洲第一区| 波多野结衣久久高清免费| 精品亚洲福利一区二区| 亚洲AⅤ永久无码精品AA| 国产久爱免费精品视频| 亚洲av无码国产精品夜色午夜| 无码av免费一区二区三区试看| 亚洲综合色丁香麻豆| 欧美男同gv免费网站观看| 午夜亚洲国产精品福利| 在线亚洲精品自拍| 在免费jizzjizz在线播| 亚洲欧洲日产国码久在线| 亚洲中文字幕成人在线| 无码精品国产一区二区三区免费| 亚洲最大在线视频| 免费观看午夜在线欧差毛片| 久久不见久久见免费影院www日本| 久久91亚洲精品中文字幕| 免费在线看v网址| 免费一级做a爰片久久毛片潮| 精品国产亚洲一区二区三区| 日韩在线播放全免费| 亚洲a∨国产av综合av下载| 亚洲精品无码鲁网中文电影| 无码国产精品一区二区免费式影视 | 亚洲高清专区日韩精品| 一二三四影视在线看片免费 | 五月天网站亚洲小说|