最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java編程實現(xiàn)排他鎖代碼詳解

 更新時間:2017年10月16日 14:33:58   作者:jacin1  
這篇文章主要介紹了Java編程實現(xiàn)排他鎖的相關內容,敘述了實現(xiàn)此代碼鎖所需要的功能,以及作者的解決方案,然后向大家分享了設計源碼,需要的朋友可以參考下。

一 .前言

某年某月某天,同事說需要一個文件排他鎖功能,需求如下:

(1)寫操作是排他屬性
(2)適用于同一進程的多線程/也適用于多進程的排他操作
(3)容錯性:獲得鎖的進程若Crash,不影響到后續(xù)進程的正常獲取鎖

二 .解決方案

1. 最初的構想

在Java領域,同進程的多線程排他實現(xiàn)還是較簡易的。比如使用線程同步變量標示是否已鎖狀態(tài)便可。但不同進程的排他實現(xiàn)就比較繁瑣。使用已有API,自然想到 java.nio.channels.FileLock:如下

/** 
   * @param file 
   * @param strToWrite 
   * @param append 
   * @param lockTime 以毫秒為單位,該值只是方便模擬排他鎖時使用,-1表示不考慮該字段 
   * @return 
   */ 
  public static boolean lockAndWrite(File file, String strToWrite, boolean append,int lockTime){ 
    if(!file.exists()){ 
      return false; 
    } 
    RandomAccessFile fis = null; 
    FileChannel fileChannel = null; 
    FileLock fl = null; 
    long tsBegin = System.currentTimeMillis(); 
    try { 
      fis = new RandomAccessFile(file, "rw"); 
      fileChannel = fis.getChannel(); 
      fl = fileChannel.tryLock(); 
      if(fl == null || !fl.isValid()){ 
        return false; 
      } 
      log.info("threadId = {} lock success", Thread.currentThread()); 
      // if append 
      if(append){ 
        long length = fis.length(); 
        fis.seek(length); 
        fis.writeUTF(strToWrite); 
      //if not, clear the content , then write 
      }else{ 
        fis.setLength(0); 
        fis.writeUTF(strToWrite); 
      } 
      long tsEnd = System.currentTimeMillis(); 
      long totalCost = (tsEnd - tsBegin); 
      if(totalCost < lockTime){ 
        Thread.sleep(lockTime - totalCost); 
      } 
    } catch (Exception e) { 
      log.error("RandomAccessFile error",e); 
      return false; 
    }finally{ 
      if(fl != null){ 
        try { 
          fl.release(); 
        } catch (IOException e) { 
          e.printStackTrace(); 
        } 
      } 
      if(fileChannel != null){ 
        try { 
          fileChannel.close(); 
        } catch (IOException e) { 
          e.printStackTrace(); 
        } 
      } 
      if(fis != null){ 
        try { 
          fis.close(); 
        } catch (IOException e) { 
          e.printStackTrace(); 
        } 
      } 
    } 
    return true; 
  } 

一切看起來都是那么美好,似乎無懈可擊。于是加上兩種測試場景代碼:

(1)同一進程,兩個線程同時爭奪鎖,暫定命名為測試程序A,期待結果:有一線程獲取鎖失敗
(2)執(zhí)行兩個進程,也就是執(zhí)行兩個測試程序A,期待結果:有一進程某線程獲得鎖,另一線程獲取鎖失敗

public static void main(String[] args) { 
    new Thread("write-thread-1-lock"){ 
      @Override 
      public void run() { 
        FileLockUtils.lockAndWrite(new File("/data/hello.txt"), "write-thread-1-lock" + System.currentTimeMillis(), false, 30 * 1000);} 
    }.start(); 
    new Thread("write-thread-2-lock"){ 
      @Override 
      public void run() { 
        FileLockUtils.lockAndWrite(new File("/data/hello.txt"), "write-thread-2-lock" + System.currentTimeMillis(), false, 30 * 1000); 
      } 
    }.start(); 
  } 

2.世界不像你想的那樣

上面的測試代碼在單個進程內可以達到我們的期待。但是同時運行兩個進程,在Mac環(huán)境(java8) 第二個進程也能正常獲取到鎖,在Win7(java7)第二個進程則不能獲取到鎖。為什么?難道TryLock不是排他的?

其實不是TryLock不是排他,而是channel.close 的問題,官方說法:

On some systems, closing a channel releases all locks held by the Java virtual machine on the 
 underlying file regardless of whether the locks were acquired via that channel or via  
another channel open on the same file.It is strongly recommended that, within a program, a unique 
 channel be used to acquire all locks on any given file. 

原因就是在某些操作系統(tǒng),close某個channel將會導致JVM釋放所有l(wèi)ock。也就是說明了上面的第二個測試用例為什么會失敗,因為第一個進程的第二個線程獲取鎖失敗后,我們調用了channel.close ,所有將會導致釋放所有l(wèi)ock,所有第二個進程將成功獲取到lock。

在經過一段曲折尋找真理的道路后,終于在stackoverflow上找到一個帖子 ,指明了 lucence 的 NativeFSLock,NativeFSLock 也是存在多個進程排他寫的需求。筆者參考的是lucence 4.10.4 的NativeFSLock源碼,具體可見地址,具體可見obtain 方法,NativeFSLock 的設計思想如下:

(1)每一個鎖,都有本地對應的文件。
(2)本地一個static類型線程安全的Set<String> LOCK_HELD維護目前所有鎖的文件路徑,避免多線程同時獲取鎖,多線程獲取鎖只需判斷LOCK_HELD是否已有對應的文件路徑,有則表示鎖已被獲取,否則則表示沒被獲取。
(3)假設LOCK_HELD 沒有對應文件路徑,則可對File的channel TryLock。

public synchronized boolean obtain() throws IOException { 
    if (lock != null) { 
      // Our instance is already locked: 
      return false; 
    } 
    // Ensure that lockDir exists and is a directory. 
    if (!lockDir.exists()) { 
      if (!lockDir.mkdirs()) 
        throw new IOException("Cannot create directory: " + lockDir.getAbsolutePath()); 
    } else if (!lockDir.isDirectory()) { 
      // TODO: NoSuchDirectoryException instead? 
      throw new IOException("Found regular file where directory expected: " + lockDir.getAbsolutePath()); 
    } 
    final String canonicalPath = path.getCanonicalPath(); 
    // Make sure nobody else in-process has this lock held 
    // already, and, mark it held if not: 
    // This is a pretty crazy workaround for some documented 
    // but yet awkward JVM behavior: 
    // 
    // On some systems, closing a channel releases all locks held by the 
    // Java virtual machine on the underlying file 
    // regardless of whether the locks were acquired via that channel or via 
    // another channel open on the same file. 
    // It is strongly recommended that, within a program, a unique channel 
    // be used to acquire all locks on any given 
    // file. 
    // 
    // This essentially means if we close "A" channel for a given file all 
    // locks might be released... the odd part 
    // is that we can't re-obtain the lock in the same JVM but from a 
    // different process if that happens. Nevertheless 
    // this is super trappy. See LUCENE-5738 
    boolean obtained = false; 
    if (LOCK_HELD.add(canonicalPath)) { 
      try { 
        channel = FileChannel.open(path.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE); 
        try { 
          lock = channel.tryLock(); 
          obtained = lock != null; 
        } catch (IOException | OverlappingFileLockException e) { 
          // At least on OS X, we will sometimes get an 
          // intermittent "Permission Denied" IOException, 
          // which seems to simply mean "you failed to get 
          // the lock". But other IOExceptions could be 
          // "permanent" (eg, locking is not supported via 
          // the filesystem). So, we record the failure 
          // reason here; the timeout obtain (usually the 
          // one calling us) will use this as "root cause" 
          // if it fails to get the lock. 
          failureReason = e; 
        } 
      } finally { 
        if (obtained == false) { // not successful - clear up and move 
                      // out 
          clearLockHeld(path); 
          final FileChannel toClose = channel; 
          channel = null; 
          closeWhileHandlingException(toClose); 
        } 
      } 
    } 
    return obtained; 
  } 

總結

以上就是本文關于Java編程實現(xiàn)排他鎖代碼詳解的全部內容,感興趣的朋友可以參閱:Java并發(fā)編程之重入鎖與讀寫鎖詳解java中的互斥鎖信號量和多線程等待機制、Java語言中cas指令的無鎖編程實現(xiàn)實例以及本站其他相關專題,希望對大家有所幫助。如有不足之處,歡迎留言指出,小編一定及時更正,給大家提供更好的閱讀環(huán)境和幫助,感謝朋友們對本站的支持

相關文章

  • Java五種方式實現(xiàn)多線程循環(huán)打印問題

    Java五種方式實現(xiàn)多線程循環(huán)打印問題

    本文主要介紹了Java五種方式實現(xiàn)多線程循環(huán)打印問題,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • Spring框架之IOC介紹講解

    Spring框架之IOC介紹講解

    IOC-Inversion of Control,即控制反轉。它不是什么技術,而是一種設計思想。這篇文章將為大家介紹一下Spring控制反轉IOC的原理,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • Java?String類和StringBuffer類的區(qū)別介紹

    Java?String類和StringBuffer類的區(qū)別介紹

    這篇文章主要介紹了Java?String類和StringBuffer類的區(qū)別,?關于java的字符串處理我們一般使用String類和StringBuffer類有什么不同呢,下面我們一起來看看詳細介紹吧
    2022-03-03
  • 一篇文章帶你入門java泛型

    一篇文章帶你入門java泛型

    這篇文章主要介紹了java泛型基礎知識及通用方法,從以下幾個方面介紹一下java的泛型: 基礎, 泛型關鍵字, 泛型方法, 泛型類和接口,感興趣的可以了解一下
    2021-08-08
  • mybatis3.3+struts2.3.24+mysql5.1.22開發(fā)環(huán)境搭建圖文教程

    mybatis3.3+struts2.3.24+mysql5.1.22開發(fā)環(huán)境搭建圖文教程

    這篇文章主要為大家詳細介紹了mybatis3.3+struts2.3.24+mysql5.1.22開發(fā)環(huán)境搭建圖文教程,感興趣的小伙伴們可以參考一下
    2016-06-06
  • Intellij Idea 多模塊Maven工程中模塊之間無法相互引用問題

    Intellij Idea 多模塊Maven工程中模塊之間無法相互引用問題

    這篇文章主要介紹了Intellij Idea 多模塊Maven工程中模塊之間無法相互引用問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-01-01
  • Java8中如何通過方法引用獲取屬性名詳解

    Java8中如何通過方法引用獲取屬性名詳解

    這篇文章主要給大家介紹了關于Java8中如何通過方法引用獲取屬性名的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-09-09
  • Java面試之如何實現(xiàn)10億數(shù)據判重

    Java面試之如何實現(xiàn)10億數(shù)據判重

    當數(shù)據量比較大時,使用常規(guī)的方式來判重就不行了,所以這篇文章小編主要來和大家介紹一下Java實現(xiàn)10億數(shù)據判重的相關方法,希望對大家有所幫助
    2024-02-02
  • Java基于Socket實現(xiàn)網絡編程實例詳解

    Java基于Socket實現(xiàn)網絡編程實例詳解

    本文主要給大家介紹的是Java基于Socket實現(xiàn)網絡編程的實例,并給大家介紹了TCP與UDP傳輸協(xié)議,有需要的小伙伴可以來參考下
    2016-07-07
  • 數(shù)據同步利器DataX簡介及如何使用

    數(shù)據同步利器DataX簡介及如何使用

    DataX?是阿里云?DataWorks數(shù)據集成?的開源版本,使用Java?語言編寫,在阿里巴巴集團內被廣泛使用的離線數(shù)據同步工具/平臺,今天給大家分享一個阿里開源的數(shù)據同步工具DataX,在Github擁有14.8k的star,非常受歡迎
    2024-02-02

最新評論

尖扎县| 龙山县| 衡东县| 婺源县| 玉田县| 外汇| 吴桥县| 化德县| 山丹县| 淳化县| 吴堡县| 中山市| 繁峙县| 宜城市| 海原县| 磐安县| 铜梁县| 五大连池市| 玛多县| 丹凤县| 韶关市| 武穴市| 泸定县| 富阳市| 沐川县| 深泽县| 贡觉县| 遵义县| 武功县| 井冈山市| 福泉市| 通渭县| 彭水| 泰顺县| 曲周县| 柳州市| 凤山县| 永修县| 孙吴县| 南阳市| 高平市|