如何在不同的应用程序级别上锁定文件?

这是场景:我有一个在servlet容器中运行的multithreadingjava Web应用程序。 应用程序在servlet容器中多次部署。 有多个servlet容器在不同的服务器上运行。

也许这个图表清楚地说明了:

server1 +- servlet container +- application1 | +- thread1 | +- thread2 +- application2 +- thread1 +- thread2 server2 +- servlet container +- application1 | +- thread1 | +- thread2 +- application2 +- thread1 +- thread2 

网络共享目录中有一个文件,所有这些线程都可以访问。 他们经常访问该文件。 大多数情况下,文件只能被这些线程读取。 但有时它是写的。

我需要一个故障安全解决方案来同步所有这些线程,以保证数据的一致性。


不起作用的解决方案(正确)

  1. 使用java.nio.channels.FileLock
    我能够使用FileLock类同步来自不同服务器的线程。 但是这对于同一进程(servlet容器)中的线程不起作用,因为文件锁在进程范围内可用。

  2. 使用单独的文件进行同步
    我可以创建一个单独的文件,指示进程正在读取或写入文件。 此解决方案适用于所有线程,但有几个缺点:

    • 性能。 创建,删除和检查文件的操作相当慢。 具有一个同步文件的低权重实现将阻止并行读取文件。
    • JVM崩溃后,同步文件将保留,需要手动清理。
    • 我们在删除网络文件系统上的文件时已经有了奇怪的问题。
  3. 使用消息
    我们可以实现一个消息传递系统,线程将用它来协调文件访问。 但这对于这个问题来说似乎太复杂了。 再说一次:表现会很糟糕。

有什么想法吗?

如果您只需要很少编写文件,那么如何使用临时名称编写文件然后使用重命名使其对读者“可见”?

但这仅适用于Unix文件系统。 在Windows上,您将需要处理某些进程打开文件(用于读取)的情况。 在这种情况下,重命名将失败。 再试一次,直到重命名成功。

我建议彻底测试这个,因为你可能会遇到拥塞:有很多读取请求,编写器任务很长时间都无法替换文件。

如果是这种情况,请让读者检查临时文件并等待下一次读取,直到文件消失。

您已经枚举了除了显而易见的解决方案之外的可能解决方案: 删除对该文件的依赖性

是否还有另一种方法让线程获取数据而不是从文件中读取数据? 如何设置某种负责协调对该信息的访问而不是让所有线程都读取该文件的进程。

A.听起来像数据库的时间:-)。 而不是让共享文件将数据存储在数据库中。

B.或者 – 分层:

  1. 使用标准同步锁定锁定进程内的线程。
  2. 使用基于文件的锁定类型锁定进程间/机器 – 例如,创建一个用于保存锁定的目录。

巢2在1内。

还有清理问题。

C.或者某种写入新文件/重命名策略,以便读者不需要锁定?

最简单的解决方案是创建另一个流程(Web服务或任何对您来说最简单的流程)。 只有这个进程读/写文件,它才会监听其他服务的读/写请求。

虽然这似乎比直接使用网络共享慢,但情况不一定如此:使用网络共享意味着使用内置于操作系统中的客户端/服务器(确实如此:发送读/写请求到提供共享的服务器)。

由于您的服务针对任务进行了优化(而不是一般的“服务文件”服务),因此甚至可能更快。

使用java.nio.channels.FileLock和ReadWriteLock。

如果我是你,我会隐藏所有业务代码中的File,FileChannel和所有FileOutputStream。 替换为我自己的简单适配器类,如DAO。

例如

 abstract class MyWriter{ private FileChannel file; public void writeSomething(byte[] b){ // get VM scope write lock here // get file lock here // do write // release file lock // release readwritelock lock } } 

您是否可以在应用程序中使用Semaphore来控制访问?

引用API“信号量通常用于限制线程数量而不是访问某些(物理或逻辑)资源”

虽然该API可能仍然是容器特定的,但分布式信号量的概念应该是可以实现的,可能使用JGroups 。

谷歌搜索“分布式java信号量”的粗略搜索出现了Jukebox ,看起来它可以解决上述问题