【问题标题】:How wait and get lock on file如何等待并锁定文件
【发布时间】:2012-12-22 10:13:08
【问题描述】:

我想等到其他程序释放对特定文件的锁定,然后我想继续打开那个解锁的文件。

我遇到了很多解决方案,但没有一个有用,这是我的代码 -

File file = new File("c:\\somelockedfile.txt");
    FileChannel channel = null;
    try{
        channel = new RandomAccessFile(file, "rw").getChannel();
    }catch(Exception e){
        e.printStackTrace();
    }
    try {
        // Get an exclusive lock on the whole file
        FileLock lock = channel.lock();
        try {
            doWithLockedFile(file);
        } finally {
            lock.release();
        }
    }catch(Exception e){
        e.printStackTrace();
    } finally {
        try{
            channel.close();
        }catch(Exception e){
            e.printStackTrace();
        }
    }

我知道什么时候运行这段代码,文件会被其他 Windows 进程锁定,所以我想等到其他进程释放锁定,然后我会继续解锁文件。如果我尝试打开锁定的文件,我会得到FileNotFoundException,比如“(The process cannot access the file because it is being used by another process)”。

在上面的代码中,我等不及要获得锁了,因为同样的异常会在“channel = new RandomAccessFile(file, "rw").getChannel();”行中抛出

请帮助我,基本上我想得到通知其他进程已释放文件锁定,直到那个时候,我想等待()。

【问题讨论】:

  • 为什么不直接使用while 循环,直到获得频道,然后在每次尝试之间使用Thread.sleep(1000)?当然,尝试次数有限。
  • 感谢 fge,我可以做到这一点,但是为什么 Java 中有 FileLock?如果我们无法在锁定的文件上创建频道,我不明白如何等待和锁定文件?我猜 channel.lock() 只是为了这个目的。
  • 请参阅stackoverflow.com/questions/4025721/java-file-locking 这可能会有所帮助
  • FileLock 的 lock() 方法会等到其他进程释放对文件的锁定吗?如果是这样,那么如何为此目的使用 FileLock。此外,如果我们可以在文件解锁时锁定文件,那么为什么 Java 告诉 lock() 会等到它没有锁定文件。
  • @home,那个帖子对我没用,我只有那个代码。 Java 中的 FileLock 说,文件可以在打开的通道的帮助下锁定。但是,如果该文件已被锁定,我如何打开频道并等待锁定。简单地说,我只会在打开频道时在锁定之前得到异常。

标签: java nio filelock filechannel


【解决方案1】:

我通过观察者收到的一些睡眠和排队文件名来等待和获取锁。

解决方案:

package dirwatch;

import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
import static java.nio.file.LinkOption.*;
import java.nio.file.attribute.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public class WatchDir {
    private final WatchService watcher;
    private final Map<WatchKey,Path> keys;
    private final boolean recursive;
    private boolean trace = false;

    private BlockingQueue<String> fileProcessingQueue;

    //******* processedFileQueue **** will be used by other threads to retrive unlocked files.. so I have 
    // kept as public final
    public final BlockingQueue<String> processedFileQueue;
    private volatile boolean closeProcessingThread;
    private volatile boolean closeWatcherThread;


    private void processFiles(){
        System.out.println("DirWatchProcessingThread Started");
        String fileName;
        outerLoop: while(!closeProcessingThread || !fileProcessingQueue.isEmpty()){
            try{
                fileName = fileProcessingQueue.poll(1000, TimeUnit.MILLISECONDS);
            }catch(InterruptedException ie){
                fileName = null;
            }

            if(fileName == null || fileName.equals("")){
                continue outerLoop;
            }

            long startTime = System.currentTimeMillis();
            innerLoop: while(true){
                FileInputStream fis = null;
                File file = new File(fileName);
                try{
                    fis = new FileInputStream(fileName);
                    break innerLoop;
                }catch(FileNotFoundException fnfe){
                    if(!file.exists() || file.isDirectory()){
                        System.out.println("File: '"+fileName+"has been deleted in file system or it is not file. Not processing this file.");
                        continue outerLoop;
                    }
                    try{
                        Thread.sleep(WatchDirParameters.millisToPuaseForFileLock);
                    }catch(InterruptedException ie){
                    }
                    if((System.currentTimeMillis() - startTime) > WatchDirParameters.millisToSwapFileForUnlocking){
                        if(fileProcessingQueue.offer(fileName)){
                            continue outerLoop;
                        }else{
                            startTime = System.currentTimeMillis();
                            continue innerLoop;
                        }
                    }
                }finally{
                    if(fis != null){
                        try{
                            fis.close();
                        }catch(IOException ioe){
                            ioe.printStackTrace();
                        }
                    }
                }
            }

            System.out.println("Queuing File: "+fileName);
            processedLoop:while(true){
                try{
                    if(processedFileQueue.offer(fileName, 1000, TimeUnit.MILLISECONDS)){
                        break processedLoop;
                    }
                }catch(InterruptedException ie){
                    //ie.printStackTrace();
                }
            }
        }
        closeWatcherThread = true;
        closeProcessingThread = true;
        System.out.println("DirWatchProcessingThread Exited");
    }

    /**
     * Process all events for keys queued to the watcher
     */
    private void processEvents(){
        System.out.println("DirWatcherThread started.");
        while(!closeWatcherThread) {
            // wait for key to be signalled
            WatchKey key;
            try {
                key = watcher.take();
            } catch (InterruptedException x) {
                // if we are returning from these method, it means we no longer wants to watch directory
                // we must close thread which may be waiting for file names in queue
                continue;
            }catch(ClosedWatchServiceException cwse){
                break;
            }

            Path dir = keys.get(key);
            if (dir == null) {
                System.err.println("WatchKey not recognized!!");
                continue;
            }

            try{
                for (WatchEvent<?> event: key.pollEvents()) {
                    WatchEvent.Kind kind = event.kind();

                    if (kind == OVERFLOW) {
                        continue;
                    }

                    // Context for directory entry event is the file name of entry
                    WatchEvent<Path> ev = cast(event);
                    Path name = ev.context();
                    Path child = dir.resolve(name);
                    if(kind.equals(ENTRY_CREATE)){
                        // if directory is created, and watching recursively, then
                        // register it and its sub-directories
                        if (recursive) {
                            try {
                                if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
                                    registerAll(child);
                                    continue;
                                }
                            } catch (IOException x) {
                                // ignore to keep sample readbale
                            }
                        }
                        while(true){
                            if(fileProcessingQueue.remainingCapacity() < 2){
                                // if only one last can be inserted then don't queue this we need 1 empty space in queue
                                // for swaping file names..
                                // sleep for some time so processing thread may have made some rooms to queue in fileQueue
                                // this logic will not create any problems as only one this thread is inserting in queue
                                try{
                                    Thread.sleep(200);
                                }catch(InterruptedException ie){
                                }
                                continue;
                            }
                            if(!fileProcessingQueue.offer(child.toString())){
                                // couldn't queue this element by whatever reason.. we will try to enqueue again by continuing loop
                                continue;
                            }else{
                                // file name has been queued in queue
                                break;
                            }
                        }
                    }
                }
                // reset key and remove from set if directory no longer accessible
                boolean valid = key.reset();
                if (!valid) {
                    keys.remove(key);

                    // all directories are inaccessible
                    if (keys.isEmpty()) {
                        break;
                    }
                }
            }catch(ClosedWatchServiceException cwse){
                break;
            }

        }
        closeProcessingThread = true;
        closeWatcherThread = true;
        System.out.println("DirWatcherThread exited.");
    }

    public void stopWatching(){
        try{
            watcher.close();
        }catch(IOException ioe){
        }
        closeProcessingThread = true;
        closeWatcherThread = true;
    }

    public static WatchDir watchDirectory(String dirName, boolean recursive) throws InvalidPathException, IOException, Exception{
        try{
            Path dir = Paths.get(dirName);
            final WatchDir watchDir = new WatchDir(dir, recursive);
            watchDir.closeProcessingThread = false;
            watchDir.closeWatcherThread = false;
            new Thread(new Runnable() {
                public void run() {
                    watchDir.processFiles();
                }
            }, "DirWatchProcessingThread").start();
            new Thread(new Runnable() {
                public void run() {
                    watchDir.processEvents();
                }
            }, "DirWatcherThread").start();
            return watchDir;
        }catch(InvalidPathException ipe){
            throw ipe;
        }catch(IOException ioe){
            throw ioe;
        }catch(Exception e){
            throw e;
        }
    }

    @SuppressWarnings("unchecked")
    private static <T> WatchEvent<T> cast(WatchEvent<?> event) {
        return (WatchEvent<T>)event;
    }

    /**
     * Register the given directory with the WatchService
     */
    private void register(Path dir) throws IOException {
        //WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
        WatchKey key = dir.register(watcher, ENTRY_CREATE);
        if (trace) {
            Path prev = keys.get(key);
            if (prev == null) {
                System.out.format("register: %s\n", dir);
            } else {
                if (!dir.equals(prev)) {
                    System.out.format("update: %s -> %s\n", prev, dir);
                }
            }
        }
        keys.put(key, dir);
    }

    /**
     * Register the given directory, and all its sub-directories, with the
     * WatchService.
     */
    private void registerAll(final Path start) throws IOException {
        // register directory and sub-directories
        Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                register(dir);
                return FileVisitResult.CONTINUE;
            }
        });
    }

    /**
     * Creates a WatchService and registers the given directory
     */
    private WatchDir(Path dir, boolean recursive) throws IOException {
        fileProcessingQueue = new ArrayBlockingQueue<String>(WatchDirParameters.fileQueueSize, false);
        processedFileQueue = new ArrayBlockingQueue<String>(WatchDirParameters.fileQueueSize, false);
        this.watcher = FileSystems.getDefault().newWatchService();
        this.keys = new HashMap<WatchKey,Path>();
        this.recursive = recursive;
        //CreateTxtFile.createFile(dir, 1);
        if (recursive) {
            System.out.format("Scanning %s ...\n", dir);
            registerAll(dir);
            System.out.println("Done.");
        } else {
            register(dir);
        }

        // enable trace after initial registration
        this.trace = true;
    }
}

配置类:

package dirwatch;

public class WatchDirParameters {
    public static final int millisToPuaseForFileLock = 200;
    public static final int fileQueueSize = 500;
    public static final int millisToSwapFileForUnlocking = 2000;
}

【讨论】:

  • 如果您能发布您的工作解决方案以避免这种情况,那就太好了:xkcd.com/979
  • @AlexanderWeinert 我希望这可以帮助你。 :)
【解决方案2】:

也许创建一个布尔值,在 catch 部分将其设置为 true(锁定),如果您到达代码的最后一段,则将其设置为 false,这意味着它已解锁并且一切正常。最后把整个事情放在一个 do{}while(locked);祝你好运。

【讨论】:

  • 感谢 Jeroen,我通过观察者收到的一些睡眠和排队文件名来等待和获取锁。
猜你喜欢
  • 2021-04-12
  • 2011-11-29
  • 1970-01-01
  • 2012-04-27
  • 2017-02-15
  • 1970-01-01
  • 2010-10-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多