AnkitSablok
AnkitSablok

Reputation: 3159

How to synchronize access to a folder using java?

The situation is as follows in my application I have 2 threads that run parallely, the purpose of one of the threads is to capture screenshots and the purpose of the second thread is to rename the screenshots that have been saved in a specific folder by the first thread - the code for the application is as follows -:

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();
            }
        }
    }

}

I want the access to the folder to be synchronized that is when one process writes the images to the folder after that the other process should be able to rename the images in the folder. But I am not able to synchronize access to the folder in which the screenshots are written and read, at some point of time using the above code gives a FileNotFoundException with the error that some other process is using the file. How can I resolve this problem.

Thanks in advance.

Upvotes: 3

Views: 2535

Answers (4)

Andremoniy
Andremoniy

Reputation: 34900

Create a shared queue between two threads LinkedBlockingQueue.

From the thread CaptureScreenshots put into this queue the newly created File objects.

From the thread RenameScreenShots sequentially read from this queue prepared File objects and process them.

UPD: If you are afraid of that billions of image file will eat a lot of memory by their File descriptor, you can apply such an algorithm enhancing:

  1. Create subfolders in a folder, which contains your image files and put image files into this subfolders.

  2. Name those subfolders with integer names: 1, 2, 3, ... , 89.

  3. Artificially limit number of files in each subfolder. When the number of files reaches the limit, just increase the name-number of the subfolder, create a new one and go on.

  4. Instead of putting File descriptor for each image file to LinkedBlockingQueue, put there Integer objects, where each will correspond to filled subfolder with same name.

  5. Inside RenameScreenShots take new element from LinkedBlockingQueue, consider this element as a subfolder name and process all quietly files within this subfolder.

UPD-2 The scheme introduced in my UPD-1 could be more easily implemented using a shared synchornized getter of some integer value, which will correspond to the last number of the processed subfolder.

Upvotes: 2

BigPotato
BigPotato

Reputation: 323

maybe you can try file lock. each file can be a file lock.

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

when you write screanshot to a file such as A.shot, you create the file A.shot, and hold the file lock of A.shot, then write data into it. when the file is finished, release the file lock.

the rename process should try to get the file lock first, if succeed, then do rename work. if the file lock can not be got (as the write thread have not released the lock), then wait.

hope it's useful. :-)

Upvotes: 2

TheWhiteRabbit
TheWhiteRabbit

Reputation: 15768

Why dont you have a Mutex and based on the Mutex controll the behaviour of the Threads accessing the Folder ??

Upvotes: 0

pap
pap

Reputation: 27614

If the actions are synchronized, that is they are run serially, then you really should not have two separate threads for it.

Do write and rename sequentially in the same thread and eliminate the need for synchronization.

Upvotes: 0

Related Questions