5同步工具類

同步容器類

常見同步類

  1. Vector

  2. Hashtable

  3. Collections.synchronizedXxx工廠方法創(chuàng)建的封裝容器

    注:Vector和Hashtable是早期JDK的部分,Collections.synchronizedXxx是JDK1.2添加的

同步容器實現(xiàn)線程安全的方式

  • 使用自身的鎖來保護它的每個方法
  • 將他們的狀態(tài)封裝起來,并對每個公有方法都進行同步,使得每次只有一個線程能訪問容器的狀態(tài)

同步容器類的問題

  • 容器上的復合操作存在線程安全問題

  • 常見的復合操作:

    1. 迭代(反復訪問元素,直到遍歷完容器內(nèi)的所有元素)
    2. 跳轉(zhuǎn)(根據(jù)指定順序找到當前元素的下一個元素)
    3. 條件運算(例如:若沒有則添加)
    //查詢vector.size()狀態(tài)可能是失效的
    for(int i=0; i<vector.size(); i++) {
        doSomething(vector.get(8));
    }
    
      ```
      /**
       * 先檢查后查詢的復合操作
       */
      public static Object getLast(Vector list) {
          //查詢list.size()的狀態(tài)可能是失效的
          int lastIndex = list.size() - 1;
          return list.get(lastIndex);
      }
      ```
    

解決復合操作線程不安全的方法

  • 客戶端在復合操作上加鎖(同步容器使用自身的鎖來保護它的每個方法)
sychronized(vector) {
    for(int i=0; i<vector.size(); i++) {
        doSomething(vector.get(8));
    }   
}
public static Object getLast(Vector list) {
    synchronized(list) {
        int lastIndex = list.size() - 1;
    return list.get(lastIndex);   
    }
}

迭代器與ConcurrentModificationException

  • 容器迭代的方式

    1. 直接使用Iterator
    2. 使用Java5.0引入的for-each
  • 迭代器的問題

    • 在同步容器的迭代器是并沒有考慮到并發(fā)修改的問題,并且他們表現(xiàn)出的行為是“及時失?。╢ail-fast)”,這意味著,當他們發(fā)現(xiàn)容器在迭代過程中被修改時,就會拋出一個ConcurrentModificationException異常
    • 這種“及時失敗”的迭代器并不是一種完備的處理機制,而只是“善意地”捕獲并發(fā)錯誤,因此只能作為并發(fā)問題的預警指示
  • 解決ConcurrentModificationException問題

    • “克隆”容器,并在副本上進行迭代

      注:克隆過程中仍然需要對容器加鎖;克隆容器時存在顯著的性能開銷;這種方式的好壞取決于多個因素,包括容器大小,在每個元素上執(zhí)行的工作,迭代操作相對于容器其他操作的調(diào)用頻率,以及在響應時間和吞吐量等方面的需求

  • 常見隱藏的迭代

    1. 容器的hashCode和equals方法會間接執(zhí)行迭代操作,當容器作為另一個容器的元素或鍵時就會出現(xiàn)這種情況

    2. containsAll、removeAll和retainAll等方法

    3. 將容器作為參數(shù)的構(gòu)造函數(shù)

      注:所有間接的迭代操作都可能拋出ConcurrentModificationException

并發(fā)容器

ConcurrentHashMap與Hashtable的區(qū)別

  • 相同點:都是線程安全的容器
  • 不同點
    1. 同步方式不同:前者分段同步且讀取操作不同步,并發(fā)性能高;后者全表同步,并發(fā)性能低
    2. 內(nèi)存消耗:前者占用內(nèi)存大,因為分段,內(nèi)部容器較多或占用更多額外內(nèi)存
    3. 迭代器一致性:前者迭代器具有弱一致性,因為讀取非同步,所以讀取的結(jié)果只包含創(chuàng)建迭代器時已有的元素;后者因為是全表同步,所以保證了迭代器的強一致性
    4. 增加API:前者增加了一些對常見復合操作的支持,例如“若沒有則添加”、替換以及有條件刪除
    5. 弱化API:前者弱化了size()、isEmpty()方法的語義以反映容器的并發(fā)性,這些方法的結(jié)果可能是失效的
    6. 迭代異常:迭代時不會出現(xiàn)ConcurrentModificationException,后者會出現(xiàn)異常
  • 并發(fā)容器替代同步容器
    • ConcurrentHashMap <- HashTable、Collections.synchronizedMap(Map)
    • ConcurrentSkipListMap <- Collections.synchronizedMap(TreeMap)
    • ConcurrentSkipListSet <- Collections.synchronizedSet(TreeSet)

CopyOnWriteArrayList特點

  1. 寫操作復制內(nèi)部容器,讀操作讀取的是內(nèi)部容器副本,迭代時不會出現(xiàn)ConcurrentModificationException
  2. 適合于讀操作多,寫操作少的多線程環(huán)境

中斷

概念

  • 中斷是一種用于取消線程操作的協(xié)作機制;一個線程不能強制其他線程停止正在執(zhí)行的操作而去執(zhí)行其他的操作,但是要求(通知)其他線程執(zhí)行到某個可以暫停的地方停止正在執(zhí)行的操作,前提是別的線程愿意停止當前的操作

中斷方法介紹

  1. isInterruputed 對象方法,獲取線程的中斷狀態(tài)

    注:線程死了后,該方法始終返回false

  2. interrupt 對象方法,設置線程的中斷狀態(tài)

    注:如果線程阻塞在Object的wait方法或Thread的join、sleep方法上,調(diào)用此方法將清除被阻塞線程的中斷狀態(tài),并且調(diào)用這些方法的地方將收到一個InterruptException

  3. interrupted 靜態(tài)方法,返回當前線程的中斷狀態(tài),并清除中斷狀態(tài)、

    注:線程死了后,該方法始終返回false

中斷異常InterruptedException處理

  • 傳遞InterruptException:不捕獲該異常,或者捕獲該異常然后在執(zhí)行某種簡單的清理工作后再次拋出這個異常
  • 恢復中斷:有時候不能拋出InterruptException,例如當代碼是Runnable的一部分時,這種情況下必須捕獲InterruptException,并通過調(diào)用當前線程的interrupt方法恢復中斷狀態(tài),將中斷信息告知調(diào)用棧中更高層的代碼

同步工具類

概念

  • 同步工具類可以是任何對象,只要他根據(jù)其自身狀態(tài)來協(xié)調(diào)線程的控制流;例如阻塞隊列可以作為同步工具類

常用同步工具類

  1. CountDownLatch(閉鎖)
    • 延遲線程的進度,直到其達到終止狀態(tài),所有線程將釋放進度,當其到達結(jié)束狀態(tài)后,將不會再改變狀態(tài)
  2. FutureTask(也可以當做閉鎖)
    • 可以返回計算結(jié)果的任務
    • 包括三種狀態(tài):等待運行(Waiting to run)、正在運行(Running)、運行完成(Completed)
    • 執(zhí)行完成表示計算的所有結(jié)束方式:正常結(jié)束、由與取消結(jié)束、由與異常結(jié)束;任務進入完成狀態(tài)后將會停止在這個狀態(tài)上
    • FutureTask表示的計算通過Callable接口來實現(xiàn)
      • Callable可以拋出受檢查的或未受檢查的異常,并且任何代碼都可能拋出Error
      • 無論任務代碼拋出什么異常,都會被封裝到一個ExecutionException中,并在Future.get中被重新拋出
      • 上面兩條將使get代碼變得復雜,因為不僅需要處理可能出現(xiàn)的ExecutionException以及未受檢查的CancellationException,而且還由于ExecutionException是做為一個Throwable類返回的
    • 優(yōu)點:提前啟動計算,可以減少等待結(jié)果需要的時間
  3. Semaphore
    • 對資源施加邊界
  4. CyclicBarrier
    • 與閉鎖相同點:柵欄類似于閉鎖,他能阻塞一組線程直到某個事件發(fā)生
    • 與閉鎖不同點:閉鎖是一次性的,柵欄可多次使用;閉鎖用于等待事件,而柵欄用于等待其他線程
  5. Exchanger
    • 它是一種Two-Party柵欄,各方在柵欄位置上交換數(shù)據(jù)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 并發(fā)與多線程是每個人程序員都頭疼的內(nèi)容,幸好Java庫所提供了豐富并發(fā)基礎(chǔ)模塊,這些多線程安全的模塊作為并發(fā)工具類...
    登高且賦閱讀 1,316評論 0 8
  • 每一個想學習Java多線程的人,手里至少有這本書或者至少要看這本書。強烈建議大家多看幾遍。 代碼中比較容易出現(xiàn)bu...
    玥玥籽閱讀 979評論 0 0
  • 本文是我自己在秋招復習時的讀書筆記,整理的知識點,也是為了防止忘記,尊重勞動成果,轉(zhuǎn)載注明出處哦!如果你也喜歡,那...
    波波波先森閱讀 11,639評論 4 56
  • 如果你在亞馬遜搜索java相關(guān)的書,本書排名是非??壳暗?,豆瓣的評分也很高。剛好我最近忙找工作,也需要復習并發(fā)相關(guān)...
    whiledoing閱讀 4,616評論 1 6
  • 睡眼惺忪。早六點半響了十分鐘的鬧鐘自己壓根沒聽到,等到乒乓的又一首生活奏鳴曲不斷侵擾耳膜終于將我喚起時,時間已經(jīng)是...
    yangzhian閱讀 249評論 0 0

友情鏈接更多精彩內(nèi)容