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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21033112/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 06:05:15  来源:igfitidea点击:

How to correctly make a thread safe Singleton Factory in Java?

javasingletonfactory-pattern

提问by

This is the first time I am writing a Factoryclass. Below is my Factory class, I am not sure whether this is the correct way of making thread safe Singleton Factory class or not. I will be returning instance of my Client using this factory?

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

And here is my TestClient class -

这是我的 TestClient 类 -

public class TestClient implements IClient {


}

And this is how I am going to use my factory -

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

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

采纳答案by Jakub Kubrynski

In fact your factory isn't thread safe, because in race condition you can have more than one ClientFactory living in application. Lets assume two threads:

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

  1. ThreadA is evaluating condition 'if (instance == null)' and instance is null, so it enters statement
  2. ThreadB is evaluating condition 'if (instance == null)' and instance is null (because A didn't make to instantiate it), so it enters statement
  3. ThreadA creates new ClientFactory() and returns it
  4. ThreadB creates new ClientFactory() and returns it
  5. Now we have more than one ClientFactory in application. Of course other threads trying to retrieve instance some time later will always return single instance.
  1. ThreadA 正在评估条件 'if (instance == null)' 并且实例为 null,因此它进入语句
  2. ThreadB 正在评估条件 'if (instance == null)' 并且 instance 为 null(因为 A 没有实例化它),所以它进入语句
  3. ThreadA 创建新的 ClientFactory() 并返回它
  4. ThreadB 创建新的 ClientFactory() 并返回它
  5. 现在我们在应用程序中有不止一个 ClientFactory。当然,稍后尝试检索实例的其他线程将始终返回单个实例。

In my opinion the easiest way to write singleton in Java is to use enum. In your case it will looks:

在我看来,在 Java 中编写单例的最简单方法是使用枚举。在您的情况下,它将看起来:

public enum ClientFactory {
  INSTANCE;

  public Company getClient() {
    return new Company();
  }
}

And usage:

和用法:

ClientFactory.INSTANCE.getClient()

回答by peter.petrov

Your factory is a perfect Singleton (it is just that it is not thread-safe).

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

回答by Bob Kuhar

Singletons and Factories are different things. To property construct a Singleton, I guess you could think of its getInstance() method as a factory. Factories make "things". Singleton means that there will only ever be 0 or exactly 1 of these "things" in existence at any time.

单身人士和工厂是不同的东西。要对单例进行属性构造,我想您可以将其 getInstance() 方法视为工厂。工厂制造“东西”。单例意味着在任何时候都只会有 0 个或恰好 1 个这些“事物”存在。

If you are trying to make a proper Singleton, it is surprisingly cumbersome to do this in a Thread-safe manner in Java. Without synchronization or other thread-safe countermeasures, the code you list above has a subtle race-condition around the check-then-set code to initialize ClientFactory instance variable. There are two ways around this race-condition. Which way you pick is largely gated by how expensive it is to go through the ClientFactory constructor. My constructors are typically lightweight, so I go the path of avoiding the need for synchronization all together.

如果您正在尝试制作一个合适的单例,那么在 Java 中以线程安全的方式执行此操作会令人惊讶地繁琐。如果没有同步或其他线程安全对策,您上面列出的代码在检查然后设置代码周围有一个微妙的竞争条件来初始化 ClientFactory 实例变量。有两种方法可以解决这种竞争条件。您选择哪种方式很大程度上取决于通过 ClientFactory 构造函数的成本。我的构造函数通常是轻量级的,所以我走的是避免所有同步的需要。

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

    private ClientFactory() { }

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

If you want to be "lazy" in the construction, not building on until someone explicitly calls getInstance(), now synchronization is needed to avoid the race condition.

如果您想在构建中“懒惰”,在有人明确调用 getInstance() 之前不构建,现在需要同步以避免竞争条件。

public class ClientFactory {
    private static ClientFactory instance = null;

    private ClientFactory() { }

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

回答by gyani

Thread safe implementations(examples) on Wiki - Singleton Pattern on Wikipedia

维基上的线程安全实现(示例) -维基百科上的单例模式

As in the link above, a single-element enumtype is the best way to implement a Singleton for any Java that supports enums.

如上面的链接所示,单元素enum类型是为任何支持枚举的 Java 实现单例的最佳方式。

One of the best yet simple ones:

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

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

Source:维基百科

回答by sambean

the ClientFactory is a factory,but neither a Singleton Factory,nor a thread-safe factory. At any point,when ClientFactory.getInstance().getClinet() is invoked,it will return a new instance,so it is not absolutely a Singleton Factory.If we fix the method like this

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

private IClient iclient;

public IClient getClient() {

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

    return iclient ;
}

Then the factory is a singleton factory,but it is not thread-safe. Assume if there are more than one threads invoke getInstance,all of the threads will find that the client factory instance is null,so they will construct the instance respectively, and the problem is the same with the method getClient().

那么工厂是一个单例工厂,但它不是线程安全的。假设有多个线程调用getInstance,所有线程都会发现客户端工厂实例为null,于是分别构造实例,问题与getClient()方法相同。

It is very easy to fix it,you can declare these two method as synchronized.

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

First of all,if you really want to use factory parrtern,don't forget to hidden the client's Constructor

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

private TestClient(){
}