【问题标题】:Static variables, threads and constructor Java静态变量、线程和构造函数 Java
【发布时间】:2015-04-03 15:39:14
【问题描述】:

我从一本公认的样本书中看到了这一点,所以很难质疑,然后有一些我不明白的地方。

一个名为 DataflightsService 的类包含一个名为 FlightFileAccess 的私有静态变量,似乎每次我们为 DataflightsService 创建一个新对象时都会实例化它,因为 FlightFileAccess 在构造函数中初始化它

public class DataflightsService{
    private static FlightFileAccess fileAccess=null;

    public DataflightsService(String path){
         fileAccess=new flightFileAccess(path);
    }

    public boolean removeflight(String code){
         //We use this static instance that wraps functionality to remove a flight
         fileAccess.remove(code);
    }
}

对我而言,这意味着每次我们创建DataflightsService 的实例时,在构造函数中都会始终为静态变量FlightFileAccess 使用不同的对象

在原来的FlightFileAccess类中:我们有同步RandomAccessFile的remove方法

Class FlightFileAccess{
    private RandomAccessFile database = null;
    private boolean remove(String code){
        // Other code goes here and there

        synchronized (database) {
             //Perform deletion code
        }
    }

所以因为我们使用了FlightFileAccess 的不同引用,我们也使用了RandomAccessFile 的不同引用?

这意味着将FlightFileAccess 作为静态服务不会在此处为RandomAccessFile 上的synchronize 提供服务,因为它每次都是一个新的,所以每个DataflightsService 实例都会在随机访问文件上做他们的事情忽略同步。 与在静态启动器中实例化 FlightFileAccess 不同。我说的对吗?

我希望能提供尽可能多的解释,以提供能够实例化 DataflightsService 的最佳方式(假设每个客户端都有自己的 DataflightsService 实例),然后是例如,能够在文件上同步以进行删除,这样就不会出现多个客户端访问该文件的混乱情况。抱歉,我需要为每个客户端添加一个 DataflightsService,因为没有 cookie。

【问题讨论】:

  • 请编辑您的问题并将代码格式化为代码,然后缩进。
  • 仅仅因为您在书中找到建议,并不会自动使其成为好的建议。构造函数无条件分配一个静态字段?这听起来像是非常糟糕的设计。

标签: java multithreading variables static


【解决方案1】:

您的示例无法编译,因为构造函数的名称与类不匹配。但是,如果您打算将构造函数命名为 public DataflightsService(),那么问题的一部分是您每次创建新对象时都会覆盖静态变量。

听起来你希望这个静态变量只初始化一次。通常,您只需使用private static final FlightFileAccess fileAccess = new FlightFileAccess(); 直接分配变量,或者如果您想添加更多逻辑,就好像您有一个构造函数,您可以使用静态初始化块,如下所示:

public class Dataflights {

    private static final FlightFileAccess fileAccess;

    static {
      // Static initializer block gets run once when the class is first referenced.
      // Not usually used unless you want to add more logic besides just initializing variables.
      fileAccess = new FlightFileAccess();
    }

    private final String path;
    public final int id;

    public Dataflights(String path) {
      this.path = path;
      this.id = fileAccess.generateId();
    }

  static class FlightFileAccess {

    private volatile int nextId = 0;
    synchronized public int generateId() {
      return nextId++;
    }
  }

  public static void main(String[] args) {
    Dataflights d = new Dataflights("my/path");
    System.out.println("Id is: " + d.id);
  }
}

有很多方法可以处理争用。如果您不熟悉 Java 并发,我推荐 Java Concurrency in Practice

您在 FlightFileAccess 课程中走在正确的轨道上。我看不到细节,但您可能还想在remove() 方法的签名中使用synchronized 关键字来首先保护整个函数。然后,一旦您的工作正常进行,使用目标更紧密的 synchronize {...} 块来减少必须单线程的代码量。

【讨论】:

  • 谢谢 eric,这是一个错字,所以我修正了它,是的,基本上我想知道我的想法是否正确,因为我们需要使用相同的对象在线程上应用同步,并且在我提供的示例,它在构造函数中实例化静态对象,所以对我来说似乎是错误的,我认为在构造函数之前实例化要好得多。也感谢书籍推荐,我听说过很多次所以是的,我会有开始阅读它以了解所有多种可能性
  • 那不是静态构造函数。这是一个实例初始化程序块。它会和构造函数有同样的问题。顺便说一句,这不会编译,因为该字段是最终的。如果您的意思是静态初始化块,那么语法是static { ... }。但这不是必需的。您可以简单地直接初始化静态变量,作为其声明的一部分。
  • 感谢@JBNizet,我清理了示例中的术语和代码。
猜你喜欢
  • 1970-01-01
  • 2014-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-28
  • 1970-01-01
  • 2020-07-15
  • 1970-01-01
相关资源
最近更新 更多