4

我正在写一些东西来处理对数据库文件的并发读/写请求。

ReentrantReadWriteLock看起来很匹配。如果所有线程都访问共享的RandomAccessFile对象,我是否需要担心并发读取器的文件指针?考虑这个例子:

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Database {

    private static final int RECORD_SIZE = 50;
    private static Database instance = null;

    private ReentrantReadWriteLock lock;
    private RandomAccessFile database;

    private Database() {
        lock = new ReentrantReadWriteLock();

        try {
            database = new RandomAccessFile("foo.db", "rwd");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    };

    public static synchronized Database getInstance() {
        if(instance == null) {
            instance = new Database();
        }
        return instance;
    }

    public byte[] getRecord(int n) {
        byte[] data = new byte[RECORD_SIZE];
        try {
            // Begin critical section
            lock.readLock().lock();
            database.seek(RECORD_SIZE*n);
            database.readFully(data);
            lock.readLock().unlock();
            // End critical section
        } catch (IOException e) {
            e.printStackTrace();
        }
        return data;
    }

}

在 getRecord() 方法中,是否可以使用多个并发读取器进行以下交错?

线程 1 -> getRecord(0)
线程 2 -> getRecord(1)
线程 1 -> 获取共享锁
线程 2 -> 获取共享锁
线程 1 -> 寻求记录 0
线程 2 -> 寻求记录 1
线程 1 ->在文件指针处读取记录 (1)
线程 2 -> 在文件指针处读取记录 (1)

如果使用 ReentrantReadWriteLock 和 RandomAccessFile 确实存在潜在的并发问题,那么替代方案是什么?

4

5 回答 5

4

这是一个锁定文件和解锁文件的示例程序。

try { // Get a file channel for the file 

    File file = new File("filename");

    FileChannel channel = new RandomAccessFile(file, "rw").getChannel(); // Use the file channel to create a lock on the file.

    // This method blocks until it can retrieve the lock. 

    FileLock lock = channel.lock(); // Try acquiring the lock without blocking. This method returns // null or throws an exception if the file is already locked. 

    try { 

        lock = channel.tryLock();

    } catch (OverlappingFileLockException e){}


    lock.release(); // Close the file 

    channel.close();
} 

catch (Exception e) { } 
于 2010-09-13T13:35:26.280 回答
2

是的,正如您概述的那样,此代码未正确同步。如果从未获得写锁,则读写锁没有用;就好像没有锁一样。

使用传统的synchronized块来使查找和读取对其他线程来说是原子的,或者创建一个实例池,这些RandomAccessFile实例被借用以供单个线程独占使用,然后返回。(或者,如果您没有太多线程,则只需为每个线程专用一个通道。)

于 2009-10-19T06:36:52.377 回答
2

您可能需要考虑使用文件系统锁而不是管理您自己的锁。

调用getChannel().lock()您的 RandomAccessFile 以通过类锁定文件FileChannel。这可以防止写入访问,即使是您无法控制的进程。

于 2009-10-19T13:48:53.720 回答
1

ReentrantReadWriteLock 最多支持 65535 个递归写锁和 65535 个读锁,而不是对单个锁对象而不是方法进行操作。

分配读写锁

private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();

然后在他们身上工作...

另外:您不适合在锁定后出现异常和解锁失败。在输入方法时调用锁(如互斥锁),然后在 try/catch 块中使用 finally 部分中的解锁进行工作,例如:

public String[] allKeys() {
  r.lock();
  try { return m.keySet().toArray(); }
  finally { r.unlock(); }
}
于 2009-10-19T07:15:55.387 回答
0

好吧,8.5年是很长的时间,但我希望它不是死灵......

我的问题是我们需要访问流以尽可能原子地读写。一个重要的部分是我们的代码应该在访问同一个文件的多台机器上运行。但是,互联网上的所有示例都停留在解释如何锁定 aRandomAccessFile并且没有深入。所以我的出发点是Sam 的回答

现在,从远处看,有一定的顺序是有意义的:

  • 锁定文件
  • 打开溪流
  • 对流做任何事情
  • 关闭流
  • 释放锁

但是,为了允许在 Java 中释放锁,不能关闭流!因此,整个机制变得有点奇怪(而且是错误的?)。

为了使自动关闭工作,必须记住 JVM 以与 try-segment 相反的顺序关闭实体。这意味着流程看起来像这样:

  • 打开溪流
  • 锁定文件
  • 对流做任何事情
  • 释放锁
  • 关闭流

测试表明这不起作用。因此,中途自动关闭并以良好的 Java 1 方式完成其余工作:

try (RandomAccessFile raf = new RandomAccessFile(filename, "rwd");
    FileChannel channel = raf.getChannel()) {
  FileLock lock = channel.lock();
  FileInputStream in = new FileInputStream(raf.getFD());
  FileOutputStream out = new FileOutputStream(raf.getFD());

  // do all reading
  ...

  // that moved the pointer in the channel to somewhere in the file,
  // therefore reposition it to the beginning:
  channel.position(0);
  // as the new content might be shorter it's a requirement to do this, too:
  channel.truncate(0);

  // do all writing
  ...

  out.flush();
  lock.release();
  in.close();
  out.close();
}

请注意,使用 this 的方法仍然必须是synchronized. 否则,并行执行可能会OverlappingFileLockException在调用lock().

请分享经验,如果你有任何...

于 2018-02-16T09:24:12.050 回答