在我的程序中,我将存储每个"块"一个单独的文件中的数据。多个线程将读取和写入各种文件,我想避免因未正确同步而导致的问题。本质上,我想要一个设置,其中每个文件的行为就好像它有自己的ReadWriteLock
(两个线程可以同时写入两个不同的文件,但不能写入同一个文件)。我可以做到这一点(并做一些优化,其中ReadWriteLock
仅在需要时被实例化并且在不需要时被销毁),但是我不确定这是"正确的"要做的事。有更好的方法吗?
我已经读过在这种情况下使用FileLock
而不是是正确的做法,因为它只在不同的进程之间有效,并且不会做任何事情这种情况,因为我的所有线程都在同一个进程中。
总结一下:我想要避免的是从一个文件中读取一个线程,锁定每个其他线程对其他许多文件执行任何操作。
答案 0 :(得分:1)
这是一个示例单例类,它提供了我认为您正在描述的大部分功能。它维护ReadWriteLocks的文件名映射,并允许调用者根据文件名和READ / WRITE访问类型获取/释放锁。该类在首次使用时实例化锁,并从调用者隐藏锁对象,这可以帮助您安全地清理未使用的锁。
public enum LockRegistry {
// make our class a singleton
INSTANCE;
// map of file names to locks - You may want to change the keys to be File or
// something else, just be wary of the class's hashCode() semantics
private Map<String, ReadWriteLock> lockMap = new HashMap<String, ReadWriteLock>();
// lock to protect our registry - helps to prevent multiple threads
// from instantiating a lock with the same key
private Lock registryLock = new ReentrantLock();
// allow callers to specify the lock type they require
public enum LockType {
READ, WRITE
}
public void acquire(String fileName, LockType type) {
// lazily instantiates locks on first use
ReadWriteLock lock = retrieveLock(fileName);
switch (type) {
case READ:
lock.readLock().lock();
break;
case WRITE:
lock.writeLock().lock();
break;
default:
// handle error scenario
break;
}
}
public void release(String fileName, LockType type) {
ReadWriteLock lock = retrieveLock(fileName);
switch (type) {
case READ:
lock.readLock().unlock();
break;
case WRITE:
lock.writeLock().unlock();
break;
default:
// handle error scenario
break;
}
}
private ReadWriteLock retrieveLock(String fileName) {
ReadWriteLock newLock = null;
try {
registryLock.lock();
newLock = lockMap.get(fileName);
// create lock and add to map if it doesn't exist
if (newLock == null) {
newLock = new ReentrantReadWriteLock();
lockMap.put(fileName, newLock);
}
} finally {
registryLock.unlock();
}
return newLock;
}
}
这是一个简单的测试场景,用于显示正在运行的LockRegistry:
public class LockTester implements Runnable {
private int id;
private String fileName;
private LockType type;
public LockTester(int id, String fileName, LockType type) {
this.id = id;
this.fileName = fileName;
this.type = type;
}
@Override
public void run() {
try {
System.out.println("Consumer" + id + " acquiring " + type + " for "
+ fileName);
LockRegistry.INSTANCE.acquire(fileName, type);
System.out.println("Consumer" + id + " holding " + type + " for "
+ fileName);
// hold the lock for 2 seconds
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
LockRegistry.INSTANCE.release(fileName, type);
System.out.println("Consumer" + id + " release " + type + " for "
+ fileName);
}
}
public static void main(String[] args) {
List<Thread> testThreads = new ArrayList<Thread>();
testThreads.add(new Thread(new LockTester(1, "file1", LockType.READ)));
testThreads.add(new Thread(new LockTester(2, "file1", LockType.READ)));
testThreads.add(new Thread(new LockTester(3, "file1", LockType.WRITE)));
testThreads.add(new Thread(new LockTester(4, "file1", LockType.WRITE)));
testThreads.add(new Thread(new LockTester(5, "file2", LockType.WRITE)));
testThreads.add(new Thread(new LockTester(6, "file3", LockType.WRITE)));
testThreads.add(new Thread(new LockTester(7, "file4", LockType.WRITE)));
for (Thread t : testThreads) {
t.start();
}
}
}
希望这有帮助。