如何使用java同步对文件夹的访问?

时间:2013-01-16 07:19:28

标签: java multithreading

我的应用程序中的情况如下我有两个并行运行的线程,其中一个线程的目的是捕获屏幕截图,第二个线程的目的是重命名已保存在特定文件夹中的屏幕截图通过第一个线程 - 应用程序的代码如下 - :

CapturingAndRenamingSimultaneously.java

/**
 * Created with IntelliJ IDEA.
 * User: AnkitSablok
 * Date: 15/1/13
 * Time: 1:03 PM
 * To change this template use File | Settings | File Templates.
 */

package com.tian.screenshotcapture;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class CapturingAndRenamingSimultaneously {
    public static void main(String[] args) {

        // we use the linked blocking queue here to resolve the concurrency issues
        final BlockingQueue<File> queue = new LinkedBlockingQueue<File>(1024);

        new Thread(new Runnable() {
            @Override
            public synchronized void run() {
                try {
                    System.out.println("In the capture thread now");
                    CaptureScreenshots.captureScreenshots(queue);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public synchronized void run() {
                try {
                    while (true) {
                        System.out.println("In the rename thread now");
                        RenameScreenShots.renameScreenshots(queue);
                        Thread.sleep(5000);
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

CaptureScreenshots.java

/**
 * Created with IntelliJ IDEA.
 * User: AnkitSablok
 * Date: 15/1/13
 * Time: 12:35 PM
 * To change this template use File | Settings | File Templates.
 */

// this code is used to capture the screenshots

package com.tian.screenshotcapture;

import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.File;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class CaptureScreenshots {

    // this code is used to capture the screen shots
    public static void captureScreenshots(BlockingQueue<File> queue) throws Exception {

        String fileName = "C:\\Users\\ankitsablok\\Desktop\\Screenshots";
        int index = 0;

        for (; ; ) {
            ++index;
            Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
            Rectangle screenRectangle = new Rectangle(screenSize);
            Robot robot = new Robot();
            BufferedImage image = robot.createScreenCapture(screenRectangle);
            ImageIO.write(image, "jpg", new File(fileName + "\\i" + index + ".jpg"));
            queue.put(new File(fileName + "\\i" + index + ".jpg"));

            Thread.sleep(1000);
        }
    }

}

RenameScreenShots.java

/**
 * Created with IntelliJ IDEA.
 * User: AnkitSablok
 * Date: 15/1/13
 * Time: 12:49 PM
 * To change this template use File | Settings | File Templates.
 */

package com.tian.screenshotcapture;

import java.io.*;
import java.util.concurrent.BlockingQueue;

public class RenameScreenShots {
    public static void renameScreenshots(BlockingQueue<File> queue) throws IOException, InterruptedException {

        for (int i = 0; i < queue.size(); ++i) {

            File sourceFile = queue.take();
            System.out.println("The filename is : " + sourceFile.getName());

            if (sourceFile.getName().contains("sent")) {
            } else {
                System.out.println("The modified name of the source file is :" + sourceFile.getName().substring(0,
                        sourceFile.getName().indexOf('.'))
                        + "sent" + ".jpg");

                File newFile = new File(sourceFile.getParent() + "/" + sourceFile.getName().substring(0,
                        sourceFile.getName().indexOf('.'))
                        + "sent" + ".jpg");

                byte[] buffer = new byte[1024];

                FileInputStream fis = new FileInputStream(sourceFile);
                FileOutputStream fos = new FileOutputStream(newFile);

                int length;

                while ((length = fis.read(buffer)) > 0) {
                    fos.write(buffer, 0, length);
                }

                System.out.println("The file was deleted successfully : " + sourceFile.delete());

                fis.close();
                fos.close();
            }
        }
    }

}

我想要同步对文件夹的访问,即当一个进程将图像写入文件夹之后,另一个进程应该能够重命名文件夹中的图像。但是我无法同步访问用于编写和截取屏幕截图的文件夹,在某些时候使用上面的代码会给出FileNotFoundException以及其他一些进程正在使用该文件的错误。我该如何解决这个问题。

提前致谢。

4 个答案:

答案 0 :(得分:2)

在两个线程LinkedBlockingQueue之间创建共享。

从线程CaptureScreenshots放入此队列中新创建的File个对象。

从线程RenameScreenShots顺序读取此队列准备好的File个对象并处理它们。

UPD:如果你担心数十亿的图像文件会被File描述符占用大量内存,你可以应用这样的算法增强:

  1. 在文件夹中创建子文件夹,其中包含图像文件并将图像文件放入此子文件夹中。

  2. 将这些子文件夹和整数名称命名为:123,...,89

  3. 人为限制每个子文件夹中的文件数。当文件数量达到限制时,只需增加subfoler的name-number,创建new并继续。

  4. 不是将每个图像文件的File描述符放到LinkedBlockingQueue,而是放置Integer个对象,其中每个对应于具有相同名称的已填充子文件夹。

  5. RenameScreenShots内部从LinkedBlockingQueue获取新元素,将此元素视为子文件夹名称并处理此子文件夹中的所有安静文件。

  6. UPD-2 我的UPD-1中引入的方案可以使用某个整数值的共享synchornized getter更容易实现,这将对应于已处理的子文件夹的最后一个数字

答案 1 :(得分:2)

也许你可以尝试文件锁定。每个文件都可以是文件锁。

RandomAccessFile file = new RandomAccessFile(some_file, "rw");
FileChannel fc = file.getChannel();
FileLock lock = fc.tryLock();
....
lock.release()

当您将screanshot写入A.shot这样的文件时,您创建文件A.shot,并保存A.shot的文件锁,然后将数据写入其中。文件完成后,释放文件锁。

重命名进程应首先尝试获取文件锁,如果成功,则重命名工作。 如果无法获取文件锁(因为写线程尚未释放锁),则等待。

希望它有用。 : - )

答案 2 :(得分:0)

如果动作是同步的,那就是它们是串行运行的,那么你真的不应该有两个单独的线程。

在同一个线程中按顺序写入和重命名,无需同步。

答案 3 :(得分:0)

为什么你没有Mutex并且基于Mutex控件访问文件夹的线程的行为?