一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - Java編程實(shí)現(xiàn)排他鎖代碼詳解

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

2021-01-19 11:04jacin1 Java教程

這篇文章主要介紹了Java編程實(shí)現(xiàn)排他鎖的相關(guān)內(nèi)容,敘述了實(shí)現(xiàn)此代碼鎖所需要的功能,以及作者的解決方案,然后向大家分享了設(shè)計(jì)源碼,需要的朋友可以參考下。

一 .前言

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

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

二 .解決方案

1. 最初的構(gòu)想

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/**
   * @param file
   * @param strToWrite
   * @param append
   * @param lockTime 以毫秒為單位,該值只是方便模擬排他鎖時(shí)使用,-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;
  }

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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.世界不像你想的那樣

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

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

?
1
2
3
4
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某個(gè)channel將會(huì)導(dǎo)致JVM釋放所有l(wèi)ock。也就是說明了上面的第二個(gè)測(cè)試用例為什么會(huì)失敗,因?yàn)榈谝粋€(gè)進(jìn)程的第二個(gè)線程獲取鎖失敗后,我們調(diào)用了channel.close ,所有將會(huì)導(dǎo)致釋放所有l(wèi)ock,所有第二個(gè)進(jìn)程將成功獲取到lock。

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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;
  }

總結(jié)

以上就是本文關(guān)于Java編程實(shí)現(xiàn)排他鎖代碼詳解的全部?jī)?nèi)容,希望對(duì)大家有所幫助。如有不足之處,歡迎留言指出,小編一定及時(shí)更正,給大家提供更好的閱讀環(huán)境和幫助,感謝朋友們對(duì)本站的支持

原文鏈接:http://blog.csdn.net/jacin1/article/details/52189876

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 精品久久久久免费极品大片 | 精品香蕉99久久久久网站 | 国产成人精品午夜在线播放 | 99久久精品免费看国产四区 | 精品久久久久久久久免费影院 | 亚洲精品国产一区二区三区在 | 亚洲 欧美 中文 日韩 视频 | 无限在线看免费视频大全 | 91原创国产 | 日韩精品在线一区二区 | 亚洲AV无码乱码在线观看浪潮 | 小鸟酱喷水 | 国产成人综合一区人人 | 疯狂激吻添下边小说 | 男男gaygays黑人 | 国产精品久久久久久 | 四虎成人影院网址 | 国产黄频| 国产成人精品实拍在线 | 青青草成人在线 | 亚洲一区二区三区久久精品 | 国内精品久久久久影院网站 | 欧美成黑人性猛交xxoo | 欧美乱码视频 | aika跟黑人太猛了 | 男人日女人的逼视频 | 四虎影库紧急大通知 | 亚洲第一网色综合久久 | 天天爱天天操天天射 | 国产成人精品999在线 | 黑帮少爷爱上我第8集最新 荷兰精品女人性hd 和日本免费不卡在线v | 亚洲高清国产拍精品影院 | 4tube高清性欧美| 无码人妻精品一区二区蜜桃在线看 | 国内久久精品视频 | 免费看黄色一级 | 四虎永久在线精品免费影视 | 亚洲 日韩经典 中文字幕 | 黑人与欧洲女子性大战 | 亚洲欧美日韩国产精品一区 | 99久久999久久久综合精品涩 |