How to correctly make a thread safe Singleton Factory in Java?
这是我第一次写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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类-
1 2 3 4 | public class TestClient implements IClient { } |
这就是我如何使用我的工厂-
1 | IClient client = ClientFactory.getInstance().getClient(); |
实际上,您的工厂不是线程安全的,因为在竞争条件下,您可以在应用程序中有多个clientFactory。假设两个线程:
在我看来,在Java中编写单例最简单的方法就是使用EnUM。在您的情况下,它将看起来:
1 2 3 4 5 6 7 | public enum ClientFactory { INSTANCE; public Company getClient() { return new Company(); } } |
用途:
1 | ClientFactory.INSTANCE.getClient() |
wiki上的线程安全实现(示例)-wikipedia上的singleton模式
在上面的链接中,对于支持枚举的任何Java来说,单个元素EDCOX1×0×类型是实现单体的最佳方式。
最好但简单的一个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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; } } |
单件和工厂是不同的东西。要构造一个singleton属性,我想您可以将它的getInstance()方法视为一个工厂。工厂制造"东西"。单件意味着在任何时候,这些"事物"中只有0或1存在。
如果您试图创建一个合适的单线程,那么在Java中以线程安全的方式执行这一操作是非常麻烦的。如果没有同步或其他线程安全对策,上面列出的代码在检查周围有一个微妙的竞争条件,然后将代码设置为初始化clientfactory实例变量。有两种方法可以解决这个比赛条件。你选择哪种方式很大程度上取决于通过clientFactory构造函数的代价。我的构造器通常是轻量级的,所以我选择了避免同步需要的方法。
1 2 3 4 5 6 7 8 9 | public class ClientFactory { private static final ClientFactory instance = new ClientFactory(); private ClientFactory() { } public static ClientFactory getInstance() { return instance; } } |
如果您希望在构造中"懒惰",在有人显式调用getInstance()之前不进行构建,那么现在需要同步以避免竞争条件。
1 2 3 4 5 6 7 8 9 10 11 12 | public class ClientFactory { private static ClientFactory instance = null; private ClientFactory() { } public static synchronized ClientFactory getInstance() { if ( instance == null ) { instance = new ClientFactory(); } return instance; } } |
你的工厂是一个完美的单体(只是它不是线程安全的)。
clientFactory是一个工厂,但既不是单实例工厂,也不是线程安全工厂。在任何时候,当调用clientFactory.getInstance().getLinet()时,它将返回一个新的实例,所以它不是绝对的单例工厂。
1 2 3 4 5 6 7 8 9 10 | private IClient iclient; public IClient getClient() { if ( iclient == null ){ iclient = new TestClient(); } return iclient ; } |
那么工厂是一个单例工厂,但它不是线程安全的。假设有多个线程调用GetInstance,所有线程都将找到客户端工厂实例为空,因此它们将分别构造该实例,问题与getclient()方法相同。
很容易修复它,您可以声明这两个方法是同步的。
首先,如果您真的想使用factory parrtern,不要忘记隐藏客户的构造函数。
1 2 | private TestClient(){ } |