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类
NativeFSLock与SimpleFSLock有些不同,它的“锁”用的是“锁文件”的锁,说起来很是绕口,其实它只是给“锁文件”上一把锁:在查看某文件夹是否能被访问时,首先检查与此文件夹关联的“锁文件”的锁是否被占用,而不像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
}