【问题标题】:How to synchronize non static method variable (object) in java如何在java中同步非静态方法变量(对象)
【发布时间】:2016-09-01 08:57:24
【问题描述】:

我有一种写入文件的方法。我需要同步文件对象

class MessageFile{

public static final String  fileName="Main.html"
@AutoWired
AppConifg appconfig;

 public boolean writeToFile(String fileContent) throws  Exception{

        String path = appConfig.getNewsPath() + File.separator + fileName; // getNewsPath is non-static method
        final  File alertFile= new File(path);
        FileOutputStream out = null;
        synchronized (alertFile) {
          if (!alertFile.exists()) {
              alertFile.createNewFile();
          }
          try {
              out = new FileOutputStream(alertFile, false);
              out.write(fileContent.getBytes());
              out.flush();
          } finally {
              if (out != null) {
                  out.close();
              }
          }
      }
        return true;
    }


    }

但是上面的代码不会对文件对象采取锁排他锁,因为这个类的另一个实例可以锁定这个类并写入文件。 所以我想怎么处理这个案子? 我找到了一种解决方法,创建一个附加时间戳的临时文件名(因此临时文件名将始终是唯一的),在向其写入内容后,将首先删除原始文件,然后将临时文件重命名为原始文件名。

【问题讨论】:

  • 您的问题不清楚。为什么不能使用排他锁?

标签: java multithreading synchronization


【解决方案1】:

如果 MessageFile.class 是唯一访问该文件的对象,您可以尝试在 MessageFile.class 上进行同步。

【讨论】:

    【解决方案2】:

    您的程序没有获得文件的排他锁,因为您在一个局部变量 alertFile 上使用了 synchronized,该变量在类 MessageFile 的实例之间不共享(每个对象都有自己的 alertFile)。你有两种可能解决这个问题:

    1- 创建一些静态对象并对其进行同步(您可以使用fileName,因为它已经存在)。

    2- 在指向同一个对象的所有对象中都有一个引用(例如,在构造函数中传递)并在其上同步。

    【讨论】:

    • 请记住,初学者往往不理解变量和对象之间的区别。你说,“你在局部变量上使用synchronized”,但是当foo 是局部变量时,写synchronized(foo) 没有错。同步不对变量进行操作:它对对象进行操作。真正的问题是alertFile 变量引用了一个任何其他线程都不知道的对象。我知道这对于经验丰富的开发人员来说听起来很挑剔,但对于初学者来说,这就是顿悟和彻底、绝望的困惑之间的区别。
    【解决方案3】:

    每次运行方法时,您都在创建新的File 对象 (alertFile),因此锁什么也不做,因为每次运行方法时它都不同 - 您需要在所有方法之间共享静态 File 实例来电。

    如果每次运行该方法时path 都可能不同,您可以创建static Map<String, File> 实例并像这样使用它:

    1. 获取文件的路径。
    2. 如果没有与此路径关联的File,请创建它。
    3. 否则,从映射中恢复现有的 File 实例。
    4. 将此File用作锁并对其进行操作。

    基于修改答案的示例:

    class MessageFile{
    
        public static final String  fileName="Main.html"
        @AutoWired
        AppConifg appconfig;
    
        private static final Map<String, File> filesMap = new HashMap<>();
    
        public boolean writeToFile(String fileContent) throws  Exception{
    
            String path = appConfig.getNewsPath() + File.separator + fileName; // getNewsPath is non-static method
    
            final File alertFile;
            synchronized(filesMap) {
                if (filesMap.containsKey(path)) {
                    alertFile = filesMap.get(path);
                }
                else {
                    alertFile = new File(path);
                    filesMap.put(path, alertFile);
                }
            }
    
            FileOutputStream out = null;
            synchronized (alertFile) {
                if (!alertFile.exists()) {
                    alertFile.createNewFile();
                }
                try {
                    out = new FileOutputStream(alertFile, false);
                    out.write(fileContent.getBytes());
                    out.flush();
                } finally {
                    if (out != null) {
                        out.close();
                    }
                }
            }
            return true;
        }
    
    
    }
    

    【讨论】:

    • 请注意,File 对象基于 getNewsPath 的结果,每个实例都是独立的。
    • "如果没有File 与此路径关联,请创建它" - 在多线程上下文中,这将创建竞争条件。我建议你添加代码并展示你如何解决这个问题。
    • 添加了代码示例。
    • 这里有两个重要的问题需要提一下:1)这只是保护这个特定的写操作,而其他代码在不使用这个方法时仍然可以同时写入同一个文件。 2)这可以防止两个线程同时写入同一个文件,通过让一个线程等待另一个线程完成,因此一个线程仍将覆盖另一个线程已写入的内容,因此它可能无法解决 实际 多个线程首先尝试写入同一个文件的问题......
    【解决方案4】:

    在类级别对象上同步,即 MessageFile.class 或使用静态同步方法 wrtietofile() 。它将确保一次只有一个线程写入文件。它还保证一旦线程将整个数据写入文件,锁就会被释放。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-02-26
      • 1970-01-01
      • 2016-09-21
      • 2018-02-24
      • 2013-08-06
      • 1970-01-01
      • 2010-10-01
      相关资源
      最近更新 更多