<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

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

    • 同步容器類
    • 并發容器類
    • 生產者和消費者模式
    • 阻塞和可中斷方法
    • Synchronizer

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

    同步容器類

    同步容器類就是一些經過同步處理了的容器類,比如List有Vector,Map有Hashtable,查看其源碼發現其保證線程安全的方式就是把每個對外暴露的存取方法用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的異常,錯誤產生的邏輯如下所示:

     

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

    3)迭代器并發問題

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

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

    并發容器類

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

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

    生產者和消費者模式

    大學時學習操作系統多會為生產者和消費者模式而頭痛,也是每次考試肯定會涉及到的,而Java知道大家很憷這個模式的并發復雜性,于是乎提供了阻塞隊列(BlockingQueue)來滿足這個模式的需求。阻塞隊列說起來很簡單,就是當隊滿的時候寫線程會等待,直到隊列不滿的時候;當隊空的時候讀線程會等待,直到隊不空的時候。實現這種模式的方法很多,其區別也就在于誰的消耗更低和等待的策略更優。以LinkedBlockingQueue的具體實現為例,它的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. }  

     

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

    阻塞和可中斷方法

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

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

    Synchronizer

    Synchronizer不是一個類,而是一種滿足一個種規則的類的統稱。它有以下特性:

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

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

    1)CountDownLatch

    CountDownLatch是一種閉鎖,它通過內部一個計數器count來標示狀態,當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類實際上就是操作系統中談到的信號量的一種實現,其原理就不再累述,可見探索并發編程------操作系統篇

    具體使用就是通過其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)關卡

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

     

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

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

    <2025年7月>
    293012345
    6789101112
    13141516171819
    20212223242526
    272829303112
    3456789

    常用鏈接

    留言簿(5)

    隨筆檔案

    文章分類

    文章檔案

    相冊

    收藏夾

    J2EE

    java技術網站

    Linux

    平時常去的網站

    數據庫

    電影網站

    網站設計

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲天堂一区二区三区| 中文字幕成人免费高清在线视频 | 亚洲午夜国产片在线观看| 一级毛片全部免费播放| 免费又黄又爽又猛大片午夜 | 尤物永久免费AV无码网站| 97在线视频免费公开观看| 一级女人18片毛片免费视频| 在线观看亚洲AV日韩AV| 久久亚洲中文字幕精品有坂深雪| 亚洲一区二区三区乱码A| 日韩一级视频免费观看| 免费H网站在线观看的| 日本亚洲欧洲免费天堂午夜看片女人员 | 亚洲AV永久纯肉无码精品动漫 | 美女黄色免费网站| 亚洲精品无码永久在线观看男男| 亚洲好看的理论片电影| 国产亚洲精品a在线观看app | 99精品免费视品| 日韩电影免费在线观看网址 | 好吊妞视频免费视频| 国产免费一区二区三区| 先锋影音资源片午夜在线观看视频免费播放| 免费看美女午夜大片| 美女视频免费看一区二区| 老子影院午夜伦不卡亚洲| 亚洲欧洲免费无码| 亚洲精品国产国语| 亚洲精品国产精品国自产网站| 亚洲天堂一区二区三区四区| 亚洲综合免费视频| 亚洲精品动漫在线| 亚洲精品在线免费观看视频| 亚洲经典在线中文字幕| 亚洲视频在线一区二区三区| 亚洲第一香蕉视频| 亚洲一卡二卡三卡四卡无卡麻豆| 亚洲欧洲日产韩国在线| 亚洲一区二区三区免费观看| 亚洲AV无码久久久久网站蜜桃|