老熟女激烈的高潮_日韩一级黄色录像_亚洲1区2区3区视频_精品少妇一区二区三区在线播放_国产欧美日产久久_午夜福利精品导航凹凸

重慶分公司,新征程啟航

為企業提供網站建設、域名注冊、服務器等服務

利用Java怎么實現一個排他鎖

利用Java怎么實現一個排他鎖?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

網站建設哪家好,找創新互聯公司!專注于網頁設計、網站建設、微信開發、小程序制作、集團企業網站建設等服務項目。為回饋新老客戶創新互聯還提供了梅列免費建站歡迎大家使用!

一 .前言

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

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

二 .解決方案

1. 最初的構想

在Java領域,同進程的多線程排他實現還是較簡易的。比如使用線程同步變量標示是否已鎖狀態便可。但不同進程的排他實現就比較繁瑣。使用已有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)執行兩個進程,也就是執行兩個測試程序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環境(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. 

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

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

(1)每一個鎖,都有本地對應的文件。
(2)本地一個static類型線程安全的Set 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怎么實現一個排他鎖
文章轉載:http://www.xueling.net.cn/article/picgoj.html

其他資訊

在線咨詢
服務熱線
服務熱線:028-86922220
TOP
主站蜘蛛池模板: 亚洲国产精品无码久久久动漫 | 欧美午夜精品一区二区三区 | 97国产在线看片免费人成视频 | 91久久久爱一区二区三区 | 亚洲欧美一区二区三区久久 | 西出玉门40集全免费播放 | 麻豆一区二区三区在线观看 | 两性故事吃奶添下面 | 中文字幕第3页 | 久久精品亚洲成在人线av麻豆 | 国产毛毛片一区二区三区四区 | 日韩精品视频中文字幕 | 性欧美vr高清极品 | 亚洲熟妇色XXXXX欧美老妇Y | 久久99精品国产麻豆婷婷小说 | 国产精品VA在线观看老妇女 | 免费av大全 | 亚洲熟妇无码久久精品 | 成全视频高清免费观看在线 | 欧美麻豆久久久久久中文 | 国产原创AV在线播放不卡 | 好紧好滑好湿好爽免费视频 | 久久夜色精品国产欧美 | 国产成人av一区二区在线观看 | 免费看一级黄色大片 | 国产AⅤ无码专区亚洲AV | 张雨绮被揉到高潮下不了床 | 狠狠干夜夜操天天爽 | a级国产片| 少妇被猛男粗大的猛进出 | 人妻夫の上司犯感との中文字幕 | 国产午夜激无码Av片在线观看 | 日日射视频 | 日本天堂免费观看 | 欧美久久久国产 | 印度老妇性视频毛茸茸 | 成年美女色黄网站视频网站 | 久久中文综合 | 青青小草AV一区二区三区 | 国产日韩欧美一区二区三区在线 | 欧美三级不卡在线播放 |