如何在Java中正确地创建一个线程安全的Singleton Factory?

时间:2014-01-09 23:08:51

标签: java singleton factory-pattern

这是我第一次写Factory课程。下面是我的Factory类,我不确定这是否是制作线程安全的Singleton Factory类的正确方法。我将使用这个工厂返回我客户的实例?

public class ClientFactory {

    private static ClientFactory instance = null;   

    private ClientFactory() {

    }

    public static ClientFactory getInstance() {

        if (instance == null)
        {
            instance =  new ClientFactory();
        }

        return instance;
    }

    public IClient getClient() {

        return new TestClient();
    }
}

这是我的TestClient类 -

public class TestClient implements IClient {


}

这就是我将如何使用我的工厂 -

IClient client = ClientFactory.getInstance().getClient();

5 个答案:

答案 0 :(得分:21)

事实上,您的工厂不是线程安全的,因为在竞争条件下,您可以在应用程序中使用多个ClientFactory。让我们假设两个线程:

  1. ThreadA正在评估条件'if(instance == null)'且instance为null,因此输入语句
  2. ThreadB正在评估条件'if(instance == null)'且实例为null(因为A没有实例化它),所以它输入语句
  3. ThreadA创建新的ClientFactory()并将其返回
  4. ThreadB创建新的ClientFactory()并将其返回
  5. 现在我们在应用程序中有多个ClientFactory。当然,其他尝试稍后检索实例的线程将始终返回单个实例。
  6. 在我看来,在Java中编写单例的最简单方法是使用枚举。在你的情况下它会看起来:

    public enum ClientFactory {
      INSTANCE;
    
      public Company getClient() {
        return new Company();
      }
    }
    

    用法:

    ClientFactory.INSTANCE.getClient()
    

答案 1 :(得分:5)

Wiki上的线程安全实现(示例) - Singleton Pattern on Wikipedia

与上面的链接一样,单元素enum类型是为任何支持枚举的Java实现Singleton的最佳方式。

最简单的一个:

public class ClientFactory{
    private ClientFactory() {}

    private static ClientFactory INSTANCE=null;

    public static ClientFactory getInstance() {
        if(INSTANCE==null){
            synchronize(ClientFactory.class){
                if(INSTANCE==null) // check again within synchronized block to guard for race condition
                    INSTANCE=new ClientFactory();
            }
        }
        return INSTANCE;
    }
}

Source: Wikipedia

答案 2 :(得分:2)

你的工厂是一个完美的Singleton(只是它不是线程安全的)。

答案 3 :(得分:2)

单身人士和工厂是不同的事情。对于属性构造一个Singleton,我想你可以把它的getInstance()方法看作一个工厂。工厂制造“事物”。单身人士意味着任何时候都只有0或正好有1个这样的“事物”。

如果您正在尝试制作一个合适的Singleton,那么在Java中以线程安全的方式执行此操作会非常麻烦。如果没有同步或其他线程安全对策,上面列出的代码在check-then-set代码周围有一个微妙的竞争条件来初始化ClientFactory实例变量。围绕这种竞争条件有两种方法。您选择哪种方式主要取决于通过ClientFactory构造函数的成本。我的构造函数通常是轻量级的,所以我走的是避免同步需要的路径。

public class ClientFactory {
    private static final ClientFactory instance = new ClientFactory();

    private ClientFactory() { }

    public static ClientFactory getInstance() {
        return instance;
    }
}

如果你想在构造中“懒惰”,而不是在有人明确调用getInstance()之前建立,那么现在需要同步来避免竞争条件。

public class ClientFactory {
    private static ClientFactory instance = null;

    private ClientFactory() { }

    public static synchronized ClientFactory getInstance() {
        if ( instance == null ) {
            instance = new ClientFactory();
        }
        return instance;
    }
}

答案 4 :(得分:0)

ClientFactory是一个工厂,但既不是Singleton Factory,也不是线程安全的工厂。 在任何时候,当调用ClientFactory.getInstance()。getClinet()时,它将返回一个新的 例如,它不是绝对的Singleton Factory。如果我们修复这样的方法

private IClient iclient;

public IClient getClient() {

    if ( iclient == null ){
         iclient = new TestClient();
    }

    return iclient ;
}

然后工厂是一个单件工厂,但它不是线程安全的。 假设有多个线程调用getInstance,所有线程都会找到 客户端工厂实例为null,因此它们将分别构造实例, 问题与方法getClient()相同。

修复它很容易,你可以将这两个方法声明为同步。

首先,如果你真的想使用工厂parrtern,不要忘记隐藏客户端的构造函数

private TestClient(){
}