FileLock的疑惑和一些理解

最近碰到一个项目,有多个进程,同时操作同一目录的同一文件,笔者使用java语言。由于文件比较小,所以上线后并没有碰到什么问题。但是,我不禁想到一些问题:不同进程对同一个文件进行操作,如何保证数据的正确性。

如果在同一进程之内,我完全可以在写文件的时候,加一把对象锁,同一时刻,只能有一个线程写文件。but,我的问题是不同进程之间如何保证。

于是,我找到一个东西FileLock,关于FileLock我做了一些测试,最终也并没有达到我要的效果。希望牛人们能提供点思路,帮我解惑。

我写了一个测试类,分两次给文件写入1111111和222222,在写之前获取一把文件锁,程序结束,在finally关闭掉锁。

	public static void main(String[] args) {
		FileOutputStream fos = null;
		FileLock fLock = null;
		try {
			System.out.println("starting...");
			fos = new FileOutputStream(PATH);
			fLock = fos.getChannel().lock();
			fos.write("1111111".getBytes());
			fos.flush();
			fos.write("2222222".getBytes());//此处加了一个断点
			fos.flush();

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fLock != null)
				try {
					fLock.release();
				} catch (IOException e) {
					e.printStackTrace();
				}
			if (fos != null)
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}

注意,以上代码标注的地方,我加了一个断点。然后我发现,用windows自带的文本编辑工具,当改变文件内容的时候会报错:

到这里来看,似乎都没有什么问题。

现在,我用nodepad++打开这个文件,首先,内容已经没有了(我想大概是因为没有获取到文件锁,所以无法读取内容吧)

but,让我无法理解的地方来了,我在里面写入内容,然后保存,nodepad没有报任何异常(我想,大概是因为他自己捕捉了异常吧)

然后,我再用windows自带的文本工具打开,看到的内容是一片空白!!!!!!(到这里我已经无法理解了)

然后,有回到eclipse工程,在刚刚断点的地方,我直接让程序跑完,程序没有报出任何异常,在用windows自带的工具打开,看到内容如下:


到这里来看,似乎都没有什么问题。

现在,我用nodepad++打开这个文件,首先,内容已经没有了(我想大概是因为没有获取到文件锁,所以无法读取内容吧)



but,让我无法理解的地方来了,我在里面写入内容,然后保存,nodepad没有报任何异常(我想,大概是因为他自己捕捉了异常吧)

然后,我再用windows自带的文本工具打开,看到的内容是一片空白!!!!!!(到这里我已经无法理解了)



然后,有回到eclipse工程,在刚刚断点的地方,我直接让程序跑完,程序没有报出任何异常,在用windows自带的工具打开,看到内容如下:


从这个结果可以看出,之前写入1111111的地方,被"脏写"了。

所以,我想在不同进程之间,对文件的写操作进行保护,看来是失败了。

之后,我又做了几个尝试:

1.同样两个程序,都是用java跑的,是两个进程,第一个程序就是上面的代码里的,也同样,在上面断点;第二个程序就是直接写文件。神奇的事情又发生了,第二程序确实会抛出异常,说另一个程序正在使用,but,文件里面的内容也是被“脏写”了。最后释放断点,最后得到的结果跟上面一样。

2.两个程序,第一个用java写,更上面一样,第二程序用c++写,调试方法和上面一样,得到相同的实验结果。

那么我考虑,java这个FileLock是否是对文件的内存映射进行加锁,想了下,不对,因为如果是这样,那么这个锁仅仅在本个java进程有效;

如果是直接锁文件呢?想了下,也不太可能,windows上似乎并没有这种机制;

再来,我请教一个大牛,大牛说,windows对于文件的操作,是有一个文件映射的句柄,或者说文件句柄,所有的操作都是对这个句柄进行操作的,好吧,我也很疑惑。

所以,综上所述,就是没有解决我的问题,各位大神有啥解决思路没有?

------------------------------------------------------------------------------------------------------------------------------------------------------

先假设上面的问题不存在,FileLock是一个很完美的锁禁止,并且以下的尝试都用java语言,以下是我对FileLock的一些理解:

另外,在使用FileLock的时候,有一个lock(int start,int size,boolean isShare)的方法,带参数的哟。关于这个start和size就感觉非常奇怪,这个又是锁的啥,难道文件还是锁指定范围?答案是否定的。

首先说,FileLock有两种锁:

排他锁:只有当前线程可以读写,其他的线程或者进程对这个进行读或者写都会抛出异常

共享锁:当前线程可以操作,但是,要用RandomAccessFile并且要"rw",否则会抛异常,不信,你可以试试. 其他线程或者进程,可以读取。在锁住的时候,写,只能自己写,别人只能读

然后来说这个start和size,这个,似乎又是锁的channel,只对同一进程有效:

lock = channel.lock(0, 4, true);
lock = channel.lock(4, 8, true);

然后,这样是ok的

lock = channel.lock(0, 4, true);
lock = channel.lock(3, 8, true);

但是这样就不ok了。然后api里面提供了一个overlaps的方法,我做了一些实验,也没发现有啥效果。

再有就是,我同样的程序获得锁的地方加了一个断点,然后做最开始的那些测试,实验结果一样,不好。

然后神奇的事情又发发生了,我用下面的代码:

FileOutputStream fos = null;
FileLock fLock = null;
try {
	System.out.println("starting...");
	fos = new FileOutputStream(PATH);
	fLock = fos.getChannel().lock();
	fos.write("1111111".getBytes());
	fos.flush();
	fos.write("2222222".getBytes());
	fos.flush();
}..........

确实,会在获取lock的地方,阻塞,等待锁。

but,我再用这段代码:

FileLock lock = null;
FileChannel channel = null;
try {
	RandomAccessFile raf = new RandomAccessFile(new File(FileLocking.PATH),"rw");
	raf.seek(4);
	channel = raf.getChannel();
	lock = channel.lock(0, 4, true);
	System.out.println("2222");
}........

却发现,程序完全的执行了,并不会再加粗的地方获取等待。

好吧,继续一万点的暴击伤害。

-------------------------------------------------------------------------------------------------------------------

综上所述,我觉得,应该是我错误的使用了FileLock,请各位大牛指点批评。最主要的疑惑点在于:

1.不同进程之间,如果使用文件锁;

2.FileLock带参数的lock如何正确的使用,以及参数的含义

3.FileLock的lock到底锁的是什么对象,是否带参数的lock和不带参数的lock锁的是不同的对象

基于上面碰到的问题,如果真的要我使用文件锁的话,我会这么做:

1.在写文件的时候,尽量使用排他锁,也就是直接使用不带参数的lock或者tryLock,这是读文件的时候,如果有异常应该捕获,下次重读。那么也要求,写文件不能太频繁。

2.多个进程操作文件的时候,尽量不要操作同一个文件,否则有些不可预见的错误(是我自己知识面不够吧)


























發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章