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 阅读(2929) 评论(0)  编辑  收藏


只有注册用户登录后才能发表评论。


网站导航:
 

导航

统计

留言簿(1)

随笔分类(12)

随笔档案(19)

积分与排名

最新评论

阅读排行榜

评论排行榜