Gleeb
Gleeb

Reputation: 11289

Trying to synchronize a file writing system in java

I am trying to synchronize a file cashing on the hard disk from the database.

What i am doing in checking if the file exists and if not i fetch the file from the database and deploy it. I would not like to write the file multiple times due to a Race condition.

Here is the code i made:

IMPORTENT: This code is inside a Bean with all its meanings

@Override
public String getThumbnailPictureUrl(Design design) {
    String relativePath = String.format(THUMBNAIL_URL, design.getId(), design.getThumbnailPicture().getFileName());
    String realPath = servletContext.getRealPath("/"+relativePath); 
    logger.info("Request Thumbnail picture for design: " + design.getId());
    logger.info("Thumbnail picture relative path: " + relativePath);
    logger.info("Thumbnail picture real path: " + realPath);        
    File file = new File(realPath);
    if(!file.exists()) 
    {
        synchronized (thumbnailLock) 
        {
            if(!file.exists()) 
            {
            logger.warn("Could not fild file in path: " + realPath);
            FileAttachment pictureAttachment = design.getThumbnailPicture();
            Hibernate.initialize(pictureAttachment.getAttachment());
            Data data = (Data) pictureAttachment.getAttachment();
            file = toolBox.convertBlobToFile(data.getBlob(), file);
            logger.warn("file created in path: " + realPath);
            }
        }
    }
    return relativePath;
}

With this solution in case i don't find the file i really wont write the file 2 times as well as any other file as i am synchronizing the entire block for all threads trying to reach it even if is to write a different file.

Any suggestions?

Thanks.

Upvotes: 5

Views: 4478

Answers (2)

Peter Lawrey
Peter Lawrey

Reputation: 533520

Similar to @Avi's solution but using a ConcurrentHashMap.

private final ConcurrentMap<String, Object> map = new ConcurrentHashMap<>();

String name = file.getName();
Object lock = map.get(name);
if (lock == null) {
    map.putIfAbsent(name, new Object());
    lock = map.get(name);
}

synchronized (lock) {
    // do something
}

map.remove(name); 

Upvotes: 5

Avi
Avi

Reputation: 21858

What about creating a HashMap of locks? The key will be the file path and the value will be just an object used as lock. Let's say this map is defined like this:

Map<String, Object> locks = new Map<String, Object>();

And this is the use:

if(!file.exists()) 
{
    Object lock = null;
    synchronized (locks) {
       lock = locks.get(file.getName());
       if(lock == null) {
            lock = new Object();
            locks.put(file.getName(), lock);

       }
    }
    synchronized (lock) 
    {
        if(!file.exists()) 
        {
        logger.warn("Could not fild file in path: " + realPath);
        FileAttachment pictureAttachment = design.getThumbnailPicture();
        Hibernate.initialize(pictureAttachment.getAttachment());
        Data data = (Data) pictureAttachment.getAttachment();
        file = toolBox.convertBlobToFile(data.getBlob(), file);
        logger.warn("file created in path: " + realPath);
        }
    }
    synchronized(locks) { 
         map.remove(lock)); 
    }
}

Upvotes: 2

Related Questions