如何在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。让我们假设两个线程:
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。