3

情况如下在我的应用程序中我有 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);
        }
    }

}

重命名屏幕截图.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

4 回答 4

2

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

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

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

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

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

  2. 用整数名称命名这些子文件夹:1, 2, 3, ... , 89.

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

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

  5. RenameScreenShots取新元素 fromLinkedBlockingQueue中,将此元素视为子文件夹名称并处理此子文件夹中的所有文件。

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

于 2013-01-16T07:29:56.400 回答
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 的文件锁,然后将数据写入其中。文件完成后,释放文件锁。

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

希望它有用。:-)

于 2013-01-16T07:39:21.997 回答
0

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

在同一个线程中按顺序写入和重命名,并消除同步的需要。

于 2013-01-16T07:22:15.020 回答
0

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

于 2013-01-16T07:23:38.180 回答