Java-同步/锁定多个文件

时间:2014-07-16 02:05:24

标签: java multithreading file

在我的程序中,我将存储每个"块"一个单独的文件中的数据。多个线程将读取和写入各种文件,我想避免因未正确同步而导致的问题。本质上,我想要一个设置,其中每个文件的行为就好像它有自己的ReadWriteLock(两个线程可以同时写入两个不同的文件,但不能写入同一个文件)。我可以做到这一点(并做一些优化,其中ReadWriteLock仅在需要时被实例化并且在不需要时被销毁),但是我不确定这是"正确的"要做的事。有更好的方法吗?

我已经读过在这种情况下使用FileLock 而不是是正确的做法,因为它只在不同的进程之间有效,并且不会做任何事情这种情况,因为我的所有线程都在同一个进程中。

总结一下:我想要避免的是从一个文件中读取一个线程,锁定每个其他线程对其他许多文件执行任何操作。

1 个答案:

答案 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();
    }

}

}

希望这有帮助。