LetsCoding.cn

          天地之間有桿秤,拿秤砣砸老百姓。

          Lucene源碼分析筆記之[org.apache.lucene.store](一)

           Lock/LockFactory類系

          綜述:Lucene 的機制來實現同一文件夾的互斥訪問:當有進程訪問需要互斥訪問的文件夾時,首先查看與之關聯的是否存在,若存在則拒絕訪問;若不存在,則先上,訪問之,最后解。不同的Lock子類,具體的實現方式并不一樣。

          1.Lock/LockFactory類系的層次圖



          2.
          部分代碼說明

          Lock

          Lock本身是一個抽象類,它提供了4個方法,但它僅實現了obtain(long)這一個方法,其他三個留給了它的子類去完成。4個方法的聲明羅列如下:

          public abstract Boolean obtain() throws IOException;
          public boolean obtain(long lockWaitTimeout) throws LockObtainFailedException;;
          public abstract void release() throws IOException;
          public abstract Boolean isLocked();

           

          Lock還提供了兩個靜態變量:long LOCK_POLL_INTERVAL(默認值為1000ms)和final long LOCK_OBTAIN_WAIT_FOREVER(值為-1) ,前者為試圖獲取時的時間間隔值,后者為當lockWaitTimeout設置為該值時,obtain(long)將會無限期試圖獲取

          obtain(long)的功能為在給定的lockWaitTimeout時間內試圖獲取,一旦獲取到,則返回;超過時間則會拋出異常。其代碼及注釋如下:

           1    public boolean obtain(long lockWaitTimeout)
           2            throws LockObtainFailedException, IOException {
           3        failureReason = null;
           4        // locked試圖獲取“鎖文件”。obtain()的功能是及時返回是否能取得“鎖文件”
           5        boolean locked = obtain();
           6        // 給定參數值為負并且不等于-1,則拋出參數值設置異常
           7        if (lockWaitTimeout < 0 && lockWaitTimeout != LOCK_OBTAIN_WAIT_FOREVER)
           8            throw new IllegalArgumentException(
           9                    "lockWaitTimeout should be LOCK_OBTAIN_WAIT_FOREVER or a non-negative number (got "
          10                            + lockWaitTimeout + ")");
          11        // 設置最大睡眠次數
          12        long maxSleepCount = lockWaitTimeout / LOCK_POLL_INTERVAL;
          13        long sleepCount = 0;    // 睡眠次數累加器
          14        // 循環直到取得“鎖文件”;或者時間到,則拋出異常
          15        while (!locked) {
          16            if (lockWaitTimeout != LOCK_OBTAIN_WAIT_FOREVER
          17                    && sleepCount++ >= maxSleepCount) {        // 參數lockWaitTimeout不為-1且累計睡眠次數大于maxSleepCount,拋出異常
          18                String reason = "Lock obtain timed out: " + this.toString();
          19                if (failureReason != null{
          20                    reason += "" + failureReason;
          21                }

          22                LockObtainFailedException e = new LockObtainFailedException(
          23                        reason);
          24                if (failureReason != null{
          25                    e.initCause(failureReason);
          26                }

          27                throw e;
          28            }

          29            try {
          30                // 睡眠LOCK_POLL_INTERVAL(默認1000ms)時間
          31                Thread.sleep(LOCK_POLL_INTERVAL);
          32            }
           catch (InterruptedException e) {
          33                throw new IOException(e.toString());
          34            }

          35            // 再次試圖獲取“鎖文件”
          36            locked = obtain();
          37        }

          38        // 正常退出,
          39        return locked;
          40    }

           

          SimpleFSLock

          SimpleFSLock類中,是通過給需要訪問的文件夾另外建立一個文件的方式來實現的;查看某文件夾是否被上鎖,你需要做的僅僅是查看下與其相關的鎖文件是否存在;解鎖時只需刪除鎖文件就萬事OK了。下面是obtain()方法的代碼及注釋:

           1    public boolean obtain() throws IOException {
           2
           3        // Ensure that lockDir exists and is a directory:
           4        // 確保lockDir存在并且是文件夾類型
           5        if (!lockDir.exists()) {
           6            if (!lockDir.mkdirs())    // 如果lockDir不存在,則試圖為其建立新文件夾,建立失敗則拋出異常
           7                throw new IOException("Cannot create directory: "
           8                        + lockDir.getAbsolutePath());
           9        }
           else if (!lockDir.isDirectory()) {
          10            // 如果lockDir存在,但不是文件夾,拋出異常
          11            throw new IOException(
          12                    "Found regular file where directory expected: "
          13                            + lockDir.getAbsolutePath());
          14        }

          15        // createNewFile成功,返回true; 失敗,false;
          16        // 說明:建立成功,也就是說“鎖文件”不存在; 失敗,說明“鎖文件”已經存在,也就是說該文件夾已被上鎖
          17        return lockFile.createNewFile();
          18    }

          NativeFSLock

          NativeFSLockSimpleFSLock有些不同,它的用的是鎖文件的鎖,說起來很是繞口,其實它只是給鎖文件上一把鎖:在查看某文件夾是否能被訪問時,首先檢查與此文件夾關聯的鎖文件的鎖是否被占用,而不像SimpleFSLock僅僅查看與之相連的鎖文件是否存在。正因為如此,它解決了如果JVM異常退出時遺留的鎖文件的問題:在SimpleFSLock中,只要鎖文件存在,就被人為該文件夾被鎖,而不能被任何其他進程訪問。

          NativeFSLock額外定義了一個私有靜態變量:private static HashSet LOCK_HELD。它用來記錄鎖文件的標準路徑名(canonical path),當某文件夾的鎖文件標準路徑名存在于LOCK_HELD中,且沒有被上鎖,就說明該文件夾可被訪問;否則拒絕訪問。在解鎖時,需要從LOCK_HELD中刪除鎖文件的標準路徑名,刪除鎖文件

          NativeFSLock的代碼中包含了很多對于異常的處理,使得程序看起來很是費解。

          obtain()的主要代碼及注釋如下:

            1    public synchronized boolean obtain() throws IOException {    // 該方法被設置為同步訪問
            2        //    isLocked()為true說明“鎖文件”已被上鎖,正在被使用中
            3        if (isLocked()) {
            4            // Our instance is already locked:
            5            return false;
            6        }

            7
            8        // Ensure that lockDir exists and is a directory.
            9        if (!lockDir.exists()) {
           10            if (!lockDir.mkdirs())
           11                throw new IOException("Cannot create directory: "
           12                        + lockDir.getAbsolutePath());
           13        }
           else if (!lockDir.isDirectory()) {
           14            throw new IOException(
           15                    "Found regular file where directory expected: "
           16                            + lockDir.getAbsolutePath());
           17        }

           18
           19        String canonicalPath = path.getCanonicalPath();
           20
           21        boolean markedHeld = false;    //標記在LOCK_HELD中是否存在某“鎖文件”的路徑名
           22
           23        try {
           24
           25            // Make sure nobody else in-process has this lock held
           26            // already, and, mark it held if not:
           27
           28            synchronized (LOCK_HELD) {    // 設置LOCK_HELD的同步訪問
           29                if (LOCK_HELD.contains(canonicalPath)) {    // 如果標準路徑存在于LOCK_HELD中,說明該文件被上鎖或正在被上鎖,返回false
           30                    // Someone else in this JVM already has the lock:
           31                    return false;
           32                }
           else {
           33                    // This "reserves" the fact that we are the one
           34                    // thread trying to obtain this lock, so we own
           35                    // the only instance of a channel against this
           36                    // file:
           37                    LOCK_HELD.add(canonicalPath);    // 添加路徑名到LOCK_HELD中
           38                    markedHeld = true;    // 設置為true
           39                }

           40            }

           41
           42            try {
           43                //  建立“鎖文件”
           44                f = new RandomAccessFile(path, "rw");
           45            }
           catch (IOException e) {
           46                // On Windows, we can get intermittant "Access
           47                // Denied" here.  So, we treat this as failure to
           48                // acquire the lock, but, store the reason in case
           49                // there is in fact a real error case.
           50                failureReason = e;
           51                f = null;    // 建立失敗,則f= null
           52            }

           53
           54            if (f != null{
           55                try {
           56                    // 獲取“鎖文件”的通道
           57                    channel = f.getChannel();
           58                    try {
           59                        // 給“鎖文件”上鎖
           60                        lock = channel.tryLock();
           61                    }
           catch (IOException e) {
           62                        // At least on OS X, we will sometimes get an
           63                        // intermittant "Permission Denied" IOException,
           64                        // which seems to simply mean "you failed to get
           65                        // the lock".  But other IOExceptions could be
           66                        // "permanent" (eg, locking is not supported via
           67                        // the filesystem).  So, we record the failure
           68                        // reason here; the timeout obtain (usually the
           69                        // one calling us) will use this as "root cause"
           70                        // if it fails to get the lock.
           71                        failureReason = e;
           72                    }
           finally {        
           73                        if (lock == null{    // 如果沒有取得鎖,需關閉通道并設置其為null
           74                            try {
           75                                channel.close();    //關閉通道
           76                            }
           finally {
           77                                channel = null;    //設置為null
           78                            }

           79                        }

           80                    }

           81                }
           finally {
           82                    if (channel == null{ // 如果通道獲取失敗或者上鎖異常,關閉“鎖文件”
           83                        try {
           84                            f.close();    // 關閉“鎖文件”
           85                        }
           finally {
           86                            f = null;
           87                        }

           88                    }

           89                }

           90            }

           91
           92        }
           finally {
           93            // markedHeld為ture,但isLocked()為false,說明上鎖途中出現異常
           94            // 需刪除“鎖文件”的路徑名 
           95            if (markedHeld && !isLocked()) {
           96                synchronized (LOCK_HELD) {    // 注意同步訪問LOCK_HELD
           97                    if (LOCK_HELD.contains(canonicalPath)) {
           98                        LOCK_HELD.remove(canonicalPath);
           99                    }

          100                }

          101            }

          102        }

          103        // 經過以上過程,若成功上鎖則isLock()為true;反之,false
          104        return isLocked();
          105    }

          posted on 2008-11-10 16:26 Rolandz 閱讀(2946) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           

          導航

          統計

          留言簿(1)

          隨筆分類(12)

          隨筆檔案(19)

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 景德镇市| 光山县| 海盐县| 河池市| 宣汉县| 芮城县| 九龙县| 元氏县| 仁寿县| 塘沽区| 宝清县| 浠水县| 北碚区| 丹巴县| 临夏市| 绥棱县| 昆山市| 罗源县| 玉山县| 通化县| 介休市| 鄂托克前旗| 交城县| 葵青区| 红原县| 名山县| 福鼎市| 收藏| 花莲县| 合作市| 凤凰县| 安义县| 墨玉县| 宁德市| 伊宁市| 枣阳市| 日喀则市| 永春县| 通山县| 资阳市| 屯门区|