有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java关于单例多线程的一些东西

我使用单例读取配置:

Class Property {
      private String username;
      private String password;
      private static Property props;

      public String getUsername() {
          return username;
      }
      public String getPassword() {
          return password;
      }

      public static Property getInstance() {
          if(props == null) {
          synchronized(Property.class) {                
                   props = new Property();
                   props.initial();
              } 
          }
          return props;
      }

      public void initial() {
          Properties prop = new Properties();
          prop.load(new FileInputStream(new File("application.properties")));
          username = prop.getProperty("username");
          password = prop.getProperty("password");
      }
}

然后,在第一个线程中,我得到了属性的一个实例,如props = Property.getInstance

我这样调用方法getUsername()getPassword()

props.getUsername()/props.getPassword()

但是,这两个方法返回null。在第二个线程和之后的线程中,我可以从这两个方法中获取用户名和密码

我不知道为什么会这样。有人能帮我吗


共 (4) 个答案

  1. # 1 楼答案

    我想这是道具。上面代码中的getProperty在发布问题时是一个输入错误

    在synchronized语句之前,检查props==null。可能已经创建了props(新属性),所以不是null,但是对initial的调用还没有完成。因此,在调用getUsername/password之前,getInstance返回的对象尚未正确初始化。最简单的解决方案是使整个方法同步,但这可能是一个性能问题

    另外,在当前的实现中,您可能会创建两个单例实例。例如,如果props==null,并且最终两个线程都进入同步块。您还应该检查synchronized部分中的props是否为null,以避免创建两个实例(请参阅:http://en.wikipedia.org/wiki/Double-checked_locking

    处理多线程单例的更优雅的方法可能是按需初始化习惯用法(http://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom

  2. # 2 楼答案

    执行新的Property()后,“Props”不再为空。因此,即使未完全执行initial()-方法,也可能返回props

    请尝试以下代码:

    public static Property getInstance() {
          synchronized(Property.class) {                
          if(props == null) {
                   props = new Property();
                   props.initial();
              } 
          }
          return props;
      }
    
  3. # 3 楼答案

    antonis_wrx和Andy N.都说您的同步不够“宽”:

    1: if(props == null) {
    2:     synchronized(Property.class) {                
    3:         props = new Property();
    4:         props.initial();
    5:     } 
    6: }
    

    设想两个线程并行执行。线程A执行第1行,并且props == null返回true。紧接着,操作系统切换到线程B。线程B也执行相同的第1行,检查仍然返回true(props仍然为null)。现在两个线程都将继续并执行同步块

    因此,假设线程A获取属性上的监视器。首先初始化(第2行)并初始化道具(第2-5行)。现在线程B可以自由地获取第2行中的监视器——它执行第3行,此时,线程C调用getInstance().getUsername()

    线程C的getInstance执行第1行,该行返回false,然后返回props(这是线程B刚刚构造到new Property())。在这个未初始化的属性对象上调用getUsername()会得到所看到的空值

    请注意,对于这个示例,线程C也可能是同一个线程A(我只是想让它更易于阅读)

    您可以使用已经提出的方法(同步整个getInstance方法)来解决问题。另一种方法是使用静态块进行初始化,如下所示:

    Class Property {
        private String username;
        private String password;
        private static Property props;
    
        static {
            props = new Property();
            props.initial();
        }
    
        ...
    }
    
  4. # 4 楼答案

    将静态props更改为propatinitial()方法

    public void initial() {
        Properties prop = new Properties();
        prop.load(new FileInputStream(new File("application.properties")));
        username = prop.getProperty("username");
        password = prop.getProperty("password");
    }