Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/376.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Java中正确创建线程安全的单例工厂?_Java_Singleton_Factory Pattern - Fatal编程技术网

如何在Java中正确创建线程安全的单例工厂?

如何在Java中正确创建线程安全的单例工厂?,java,singleton,factory-pattern,Java,Singleton,Factory Pattern,这是我第一次编写Factory类。下面是我的工厂类,我不确定这是否是制作线程安全的单例工厂类的正确方法。我将返回使用此工厂的客户端实例 public class ClientFactory { private static ClientFactory instance = null; private ClientFactory() { } public static ClientFactory getInstance() { if

这是我第一次编写
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();

您的工厂是一个完美的单例(只是它不是线程安全的)。

单例和工厂是不同的东西。要构造单例属性,我想您可以将其getInstance()方法看作工厂。工厂制造“东西”。Singleton的意思是,在任何时候,这些“东西”中只有0个或正好1个存在

如果您正试图创建一个合适的单例,那么在Java中以线程安全的方式实现这一点会非常麻烦。如果没有同步或其他线程安全对策,您上面列出的代码在检查后将代码设置为初始化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;
    }
}

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

  • ThreadA正在计算条件“if(instance==null)”,实例为null,所以它进入语句
  • ThreadB正在计算条件“if(instance==null)”,而实例为null(因为A并没有对其进行实例化),所以它进入语句
  • ThreadA创建新的ClientFactory()并返回它
  • ThreadB创建新的ClientFactory()并返回它
  • 现在我们有多个ClientFactory在应用中。当然,稍后尝试检索实例的其他线程将始终返回单个实例
  • 在我看来,用Java编写singleton最简单的方法是使用enum。在您的情况下,它将看起来:

    public enum ClientFactory {
      INSTANCE;
    
      public Company getClient() {
        return new Company();
      }
    }
    
    使用方法:

    ClientFactory.INSTANCE.getClient()
    

    Wiki上的线程安全实现(示例)

    在上面的链接中,单个元素
    enum
    类型是为任何支持enum的Java实现单例的最佳方式

    最好但最简单的方法之一:

    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;
        }
    }
    

    来源:

    ClientFactory是一个工厂,但既不是单例工厂,也不是线程安全工厂。 在任何时候,当调用ClientFactory.getInstance().getClinet()时,它都会返回一个新的 实例,所以它不是绝对的单例工厂

    private IClient iclient;
    
    public IClient getClient() {
    
        if ( iclient == null ){
             iclient = new TestClient();
        }
    
        return iclient ;
    }
    
    那么这个工厂就是一个单件工厂,但它不是线程安全的。 假设有多个线程调用getInstance,则所有线程都会找到 客户端工厂实例为空,因此他们将分别构造实例, 方法getClient()也存在同样的问题

    修复它非常容易,您可以将这两个方法声明为synchronized

    首先,如果您真的想使用factory Partern,请不要忘记隐藏客户端的构造函数

    private TestClient(){
    }
    

    您只有一个工厂实例在每次调用getClient时返回一个新的客户端实例。。有什么问题吗?:)这是制作只返回一个实例的工厂模式的正确方法吗?我在读一些我也可以用的习语。@SSH忽略我之前的答案。你们工厂是个单身汉。如果那是你想要的,你就在那里。你的用法还可以啊。。这就是我要找的。。我将相应地更新我的问题。。如何创建线程安全的单例工厂?还有为什么它不是线程安全的,如果你也能向我解释一下,那么它将非常有帮助。请阅读以下内容:@JakbuK:谢谢你的建议。。你能详细解释一下为什么它不是线程安全的单例吗?我只是想理解..@SSH我已经扩展了我的答案-希望能有所帮助谢谢你的建议。。你能详细解释一下为什么它不是线程安全的单例吗?我只是想理解..@SSH假设您的实例当前为空。假设两个线程几乎同时调用静态getInstance()方法。如果线程2在线程1调用这一行之后,但在线程1调用下一行之前,调用这一行“If(instance==null)”,那么两个线程都会看到该实例为null,并且都会继续创建工厂的实例(因此现在将有2个实例),从而打破单例模式“singleton”表示“每个进程只有一个实例,“那么OP的代码并不是一个完美的单例,因为它可能导致每个进程有多个实例。谢谢..”。。你能用一个例子详细解释一下为什么我的代码不是线程安全的singleton吗?我只是想了解..考虑的情况下,2个不同的线程在几乎完全相同的时刻调用GETSimScript()。它们都看到instance==null,并且都看到new ClientFactory()将其分配给instance。他们都取得了成功,一切似乎都“正常”。但是不仅仅是创建了一个ClientFactory(),而是创建了两个;其中一个不再可访问,因为没有人持有对它的引用。您的代码不是线程安全的,因为它在检查集周围缺少同步保护。静态final变体完全避免了同步问题,并且通常工作得很好,但是静态构造函数中的失败是一个拖拉式跟踪(没有方法名的stacktrace非常好…不是)。是的,重复检查。当我想传递一些参数来构造实例时,我怀疑enum。