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

标签 java multithreading

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

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 并显示其他进程正在使用该文件的错误。我该如何解决这个问题。

提前致谢。

最佳答案

在两个线程之间创建一个共享队列LinkedBlockingQueue

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

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

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

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

  2. 用整数名称命名这些子文件夹:123、...、89.

  3. 人为限制每个子文件夹中的文件数量。当文件数量达到限制时,只需增加子文件夹的名称编号,创建一个新文件并继续。

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

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

    <

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

关于java - 如何使用java同步对文件夹的访问?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14353153/

相关文章:

java - 使用 Retrofit2 将文件上传到 AWS S3 预签名 URL

java - 创建 Java 代理 MITM

Java - 如何为多个线程同步运行函数

java - Rectangle2D#getCenterY 导致不相关的迭代器抛出 CME

C# WPF 线程

java - Solr 4.0 保留对已删除文件的引用

java - Maven:如何保护 Java 7 项目不使用针对 Java 8 编译的 jar Artifact ?

java - 在二叉树java中找到最右边的 child

c# - NServiceBus - 商业许可的建议工作线程数

xcode - 使用线程通过 Swift 更新 UI