3

状況は次のとおりです。私のアプリケーションでは、並行して実行される 2 つのスレッドがあります。スレッドの 1 つの目的はスクリーンショットをキャプチャすることであり、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 が発生します。この問題を解決するにはどうすればよいですか。

前もって感謝します。

4

4 に答える 4

2

2 つのスレッド間で共有キューを作成しますLinkedBlockingQueue

CaptureScreenshotsこのキューに入れられたスレッドから、新しく作成されたFileオブジェクト。

スレッドから、RenameScreenShotsこのキューの準備されたFileオブジェクトから順次読み取り、それらを処理します。

UPD:数十億の画像ファイルがFile記述子によって多くのメモリを消費することを恐れている場合は、そのようなアルゴリズム拡張を適用できます。

  1. 画像ファイルを含むフォルダーにサブフォルダーを作成し、画像ファイルをこのサブフォルダーに入れます。

  2. これらのサブフォルダーに整数の名前を付けます: 1, 2, 3, ... , 89.

  3. 各サブフォルダー内のファイル数を人為的に制限します。ファイル数が上限に達したら、サブフォルダーの名前番号を増やして、新しいフォルダーを作成して続行します。

  4. File各画像ファイルの記述子を に配置する代わりにLinkedBlockingQueue、そこにIntegerオブジェクトを配置します。各オブジェクトは、同じ名前の塗りつぶされたサブフォルダーに対応します。

  5. 内部RenameScreenShotsで から新しい要素を取得しLinkedBlockingQueue、この要素をサブフォルダー名と見なし、このサブフォルダー内のすべてのファイルを静かに処理します。

UPD-2 my で導入されたスキームは、処理されたサブフォルダーの最後の番号に対応する整数値UPD-1の共有ゲッターを使用して、より簡単に実装できます。synchornized

于 2013-01-16T07:29:56.400 に答える
2

ファイルロックを試すことができるかもしれません。各ファイルをファイル ロックにすることができます。

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

スクリーンショットを A.shot などのファイルに書き込む場合、ファイル A.shot を作成し、A.shot のファイル ロックを保持してから、データを書き込みます。ファイルが終了したら、ファイル ロックを解除します。

名前変更プロセスは、最初にファイル ロックの取得を試み、成功した場合は名前変更作業を行います。ファイル ロックを取得できない場合 (書き込みスレッドがロックを解放していないため)、待機します。

それが役に立つことを願っています。:-)

于 2013-01-16T07:39:21.997 に答える
0

ミューテックスを持たず、ミューテックスに基づいて、フォルダーにアクセスするスレッドの動作を制御しないのはなぜですか??

于 2013-01-16T07:23:38.180 に答える
0

アクションが同期されている場合、つまりシリアルに実行されている場合、実際には 2 つの別個のスレッドを使用するべきではありません。

同じスレッドで書き込みと名前の変更を順番に行い、同期の必要をなくします。

于 2013-01-16T07:22:15.020 に答える