Pregunta

Tengo varios subprocesos que se serializar mis objetos 'datos' a los archivos. El nombre de archivo se basa en 2 campos del objeto

  class Data {
    org.joda.DateTime time;
    String title;

    public String getFilename() {
      return time.toString() + '_' + title + ".xml";
    }

Es posible que 2 Los objetos de datos tendrán el mismo 'tiempo' y 'título', por lo que el mismo nombre de archivo.

Esto es aceptable, y estoy feliz, ya sea para ser salvados. (Son probablemente el mismo objeto de datos de todos modos si esos son lo mismo)

Mi problema es que dos (o más) hilos están escribiendo a un archivo al mismo tiempo, causando XML con formato incorrecto.

Yo tenía un aspecto en java.nio.channels.FileLock, pero es para VM-Wide bloqueo, y en concreto No es adecuado para el bloqueo dentro de la rosca.

Podría sincronizar el DataIO.class (pero que provocará una sobrecarga enorme, ya que en realidad sólo quiero sincronizar en el archivo individual).

Sincronización en el objeto del archivo será inútil, como varios objetos de archivo pueden representar el mismo sistema de archivos.

Código de la siguiente manera:

class DataIO {
  public void writeArticleToFile(Article article, String filename, boolean overwrite) throws IOException {
    File file = new File(filename);
    writeArticleToFile(article, file, overwrite);
  }

  public void writeDataToFile(Data data, File file, boolean overwrite) throws IOException {
    if (file.exists()) {
      if (overwrite) {
        if (!file.delete()) {
          throw new IOException("Failed to delete the file, for overwriting: " + file);
        }
      } else {
        throw new IOException("File " + file + " already exists, and overwrite flag is set to false.");
      }
    }

    File parentFile = file.getParentFile();
    if (parentFile != null) {
      file.getParentFile().mkdirs();
    }

    file.createNewFile();

    if (!file.canWrite()) {
      throw new IOException("You do not have permission to write to the file: " + file);
    }

    FileOutputStream fos = new FileOutputStream(file, false);
    try {
      writeDataToStream(data, fos);
      logger.debug("Successfully wrote Article to file: " + file.getAbsolutePath());
    } finally {
      fos.close();
    }
  }
}
¿Fue útil?

Solución

Usted podría pasante () la cadena que es el nombre del archivo. A continuación, sincronizar en la cadena de internados.

class DataIO {
  public void writeArticleToFile(Article article, String filename, boolean overwrite) throws IOException {
    synchronized(filename.intern()) {
       File file = new File(filename);
       writeArticleToFile(article, file, overwrite);
    }
  }

Otros consejos

Si estoy leyendo esto correctamente tiene un objeto de datos que representa un único archivo.

Se puede considerar la creación de un conjunto de rayas en función del objeto de datos. teniendo Posiblemente un ConcurrentHashMap de

ConcurrentMap<Data,Lock> lockMap = new ConcurrentHashMap<Data,Lock>();

No se cuando se quiere escribir en este objeto que puede hacer:

Lock lock = lockMap.get(someMyDataObject);
lock.lock();
try{
   //write object here
}finally{
   lock.unlock();
}

Tenga en cuenta que tendría que escribir el código hash y es igual método basado en el título y DateTime

Estoy de acuerdo que el uso de la sincronización es la técnica que debe utilizar. Lo que necesita es un objeto distinto para cada permutación de archivos, y más importante aún el mismo objeto cada vez. Una opción podría ser la creación de una clase llamada FileLock:

public class FileLock {
    DateTime time;
    String title;

    public FileLock(DateTime time, String title) {
        this.time = time;
        this.title = title;
    }

    override equals/hashCode based on those two properties

    static Hashtable<FileLock, FileLock> unqiueLocks = new Hashtable<FileLock, FileLock>();
    static lockObject = new Object();

    public static FileLock getLock(DateTime time, String title) {
        synchronized (lockObject) {
            FileLock lock = new FileLock(time, title);
            if (unqiueLocks.ContainsKey(lock)) {
                return unqiueLocks.get(lock);
            }
            else {
                unqiueLocks.put(lock, lock);
                return lock;
            }
        }
    }
}

A continuación, las personas que llaman la usarían como:

synchronized (FileLock.getLock(time, title)) {
    ...
}

Tenga en cuenta que esto tiene una pérdida de memoria ya que la tabla hash sigue creciendo con nuevas permutaciones archivo / hora. Si es necesario, se podría modificar esta técnica para que las personas que llaman de getLock también invocar un método releaseLock que se utiliza para mantener el Hashtable limpio.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top