在 Java 中实现单例模式的有效方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/70689/
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
What is an efficient way to implement a singleton pattern in Java?
提问by Riyaz Mohammed Ibrahim
What is an efficient way to implement a singleton pattern in Java?
在 Java 中实现单例模式的有效方法是什么?
采纳答案by Stephen Denne
Use an enum:
使用枚举:
public enum Foo {
INSTANCE;
}
Joshua Bloch explained this approach in his Effective Java Reloadedtalk at Google I/O 2008: link to video. Also see slides 30-32 of his presentation (effective_java_reloaded.pdf):
Joshua Bloch 在他在 Google I/O 2008 上的Effective Java Reloaded演讲中解释了这种方法:链接到视频。另见幻灯片他的介绍(30-32 effective_java_reloaded.pdf):
The Right Way to Implement a Serializable Singleton
public enum Elvis { INSTANCE; private final String[] favoriteSongs = { "Hound Dog", "Heartbreak Hotel" }; public void printFavorites() { System.out.println(Arrays.toString(favoriteSongs)); } }
实现可序列化单例的正确方法
public enum Elvis { INSTANCE; private final String[] favoriteSongs = { "Hound Dog", "Heartbreak Hotel" }; public void printFavorites() { System.out.println(Arrays.toString(favoriteSongs)); } }
Edit:An online portion of "Effective Java"says:
编辑:“Effective Java”的在线部分说:
"This approach is functionally equivalent to the public field approach, except that it is more concise, provides the serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks. While this approach has yet to be widely adopted, a single-element enum type is the best way to implement a singleton."
“这种方法在功能上等同于公共字段方法,只是它更简洁,免费提供序列化机制,并提供了对多重实例化的铁定保证,即使面对复杂的序列化或反射攻击。虽然这种方法具有尚未被广泛采用,单元素枚举类型是实现单例的最佳方式。”
回答by Neil Burroughs
Make sure that you really need it. Do a google for "singleton anti-pattern" to see some arguments against it. There's nothing inherently wrong with it I suppose but it's just a mechanism for exposing some global resource/data so make sure that this is the best way. In particular I've found dependency injection more useful particularly if you are also using unit tests because DI allows you to use mocked resources for testing purposes.
确保你真的需要它。用谷歌搜索“单例反模式”以查看一些反对它的论据。我想它本身并没有什么问题,但它只是一种公开某些全局资源/数据的机制,因此请确保这是最好的方法。特别是我发现依赖注入更有用,特别是如果您还使用单元测试,因为 DI 允许您使用模拟资源进行测试。
回答by macbirdie
回答by macbirdie
Really consider why you need a singleton before writing it. There is a quasi-religious debate about using them which you can quite easily stumble over if you google singletons in Java.
在编写之前真正考虑一下为什么需要单例。有一个关于使用它们的准宗教辩论,如果您在 Java 中使用 google 单例,您很容易偶然发现。
Personally I try to avoid singletons as often as possible for many reasons, again most of which can be found by googling singletons. I feel that quite often singletons are abused because they're easy to understand by everybody, they're used as a mechanism for getting "global" data into an OO design and they are used because it is easy to circumvent object lifecycle management (or really thinking about how you can do A from inside B). Look at things like Inversion of Control (IoC) or Dependency Injection (DI) for a nice middleground.
就我个人而言,出于多种原因,我尽量避免使用单身人士,其中大部分可以通过谷歌搜索单身人士找到。我觉得单例经常被滥用,因为它们很容易被每个人理解,它们被用作一种将“全局”数据导入 OO 设计的机制,它们被使用是因为它很容易绕过对象生命周期管理(或真正考虑如何从 B 内部做 A)。查看诸如控制反转 (IoC) 或依赖注入 (DI) 之类的东西以获得不错的中间环境。
If you really need one then wikipedia has a good example of a proper implementation of a singleton.
如果你真的需要一个,那么维基百科有一个很好的例子来正确实现单例。
回答by Jonathan
Forget lazy initialization, it's too problematic. This is the simplest solution:
忘记懒惰的初始化,它太有问题了。这是最简单的解决方案:
public class A {
private static final A INSTANCE = new A();
private A() {}
public static A getInstance() {
return INSTANCE;
}
}
回答by Aleksi Yrttiaho
If you do not need lazy loading then simply try
如果您不需要延迟加载,那么只需尝试
public class Singleton {
private final static Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() { return Singleton.INSTANCE; }
protected Object clone() {
throw new CloneNotSupportedException();
}
}
If you want lazy loading and you want your Singleton to be thread-safe, try the double-checking pattern
如果您想要延迟加载并且希望单例是线程安全的,请尝试双重检查模式
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if(null == instance) {
synchronized(Singleton.class) {
if(null == instance) {
instance = new Singleton();
}
}
}
return instance;
}
protected Object clone() {
throw new CloneNotSupportedException();
}
}
As the double checking pattern is not guaranteed to work (due to some issue with compilers, I don't know anything more about that.), you could also try to synchronize the whole getInstance-method or create a registry for all your Singletons.
由于不能保证双重检查模式有效(由于编译器的一些问题,我对此一无所知。),您还可以尝试同步整个 getInstance 方法或为所有单身人士创建一个注册表。
回答by Stu Thompson
Thread safe in Java 5+:
Java 5+ 中的线程安全:
class Foo {
private static volatile Bar bar = null;
public static Bar getBar() {
if (bar == null) {
synchronized(Foo.class) {
if (bar == null)
bar = new Bar();
}
}
return bar;
}
}
EDIT: Pay attention to the volatile
modifier here. :) It is important because without it, other threads are not guaranteed by the JMM (Java Memory Model) to see changes to its value. The synchronization does nottake care of that--it only serializes access to that block of code.
编辑:注意volatile
这里的修饰符。:) 这很重要,因为没有它,JMM(Java 内存模型)不能保证其他线程看到其值的变化。同步不会处理这个问题——它只序列化对该代码块的访问。
EDIT 2: @Bno 's answer details the approach recommended by Bill Pugh (FindBugs) and is arguable better. Go read and vote up his answer too.
编辑 2:@Bno 的回答详细说明了 Bill Pugh (FindBugs) 推荐的方法,并且可以更好地论证。去阅读并投票给他的答案。
回答by Andrew Swan
I'm mystified by some of the answers that suggest DI as an alternative to using singletons; these are unrelated concepts. You can use DI to inject either singleton or non-singleton (e.g. per-thread) instances. At least this is true if you use Spring 2.x, I can't speak for other DI frameworks.
我对一些建议将 DI 作为使用单例的替代方法的答案感到困惑;这些是不相关的概念。您可以使用 DI 注入单例或非单例(例如每线程)实例。至少如果您使用 Spring 2.x,这是正确的,我不能代表其他 DI 框架。
So my answer to the OP would be (in all but the most trivial sample code) to:
所以我对 OP 的回答是(除了最简单的示例代码之外):
- Use a DI framework like Spring, then
- Make it part of your DI configuration whether your dependencies are singletons, request scoped, session scoped, or whatever.
- 使用像Spring这样的DI框架,然后
- 无论您的依赖项是单例、请求范围、会话范围还是其他任何内容,都将其作为 DI 配置的一部分。
This approach gives you a nice decoupled (and therefore flexible and testable) architecture where whether to use a singleton is an easily reversible implementation detail (provided any singletons you use are threadsafe, of course).
这种方法为您提供了一个很好的解耦(因此灵活且可测试)架构,其中是否使用单例是一个容易可逆的实现细节(当然,前提是您使用的任何单例都是线程安全的)。
回答by Benno Richters
The solution posted by Stu Thompson is valid in Java5.0 and later. But I would prefer not to use it because I think it is error prone.
Stu Thompson 发布的解决方案在 Java5.0 及更高版本中有效。但我不想使用它,因为我认为它很容易出错。
It's easy to forget the volatile statement and difficult to understand why it is necessary. Without the volatile this code would not be thread safe anymore due to the double-checked locking antipattern. See more about this in paragraph 16.2.4 of Java Concurrency in Practice. In short: This pattern (prior to Java5.0 or without the volatile statement) could return a reference to the Bar object that is (still) in an incorrect state.
很容易忘记 volatile 语句,也很难理解为什么它是必要的。如果没有 volatile,由于双重检查锁定反模式,此代码将不再是线程安全的。有关更多信息,请参见Java Concurrency in Practice 的第 16.2.4 段。简而言之:这种模式(Java5.0 之前或没有 volatile 语句)可能会返回对(仍然)处于不正确状态的 Bar 对象的引用。
This pattern was invented for performance optimization. But this is really not a real concern anymore. The following lazy initialization code is fast and -more importantly- easier to read.
这种模式是为了性能优化而发明的。但这真的不再是一个真正的问题。以下延迟初始化代码速度快,更重要的是更易于阅读。
class Bar {
private static class BarHolder {
public static Bar bar = new Bar();
}
public static Bar getBar() {
return BarHolder.bar;
}
}
回答by Roel Spilker
Depending on the usage, there are several "correct" answers.
根据使用情况,有几个“正确”的答案。
Since java5 the best way to do it is to use an enum:
由于 java5 最好的方法是使用枚举:
public enum Foo {
INSTANCE;
}
Pre java5, the most simple case is:
在java5之前,最简单的情况是:
public final class Foo {
private static final Foo INSTANCE = new Foo();
private Foo() {
if (INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return INSTANCE;
}
public Object clone() throws CloneNotSupportedException{
throw new CloneNotSupportedException("Cannot clone instance of this class");
}
}
Let's go over the code. First, you want the class to be final. In this case, I've used the final
keyword to let the users know it is final. Then you need to make the constructor private to prevent users to create their own Foo. Throwing an exception from the constructor prevents users to use reflection to create a second Foo. Then you create a private static final Foo
field to hold the only instance, and a public static Foo getInstance()
method to return it. The Java specification makes sure that the constructor is only called when the class is first used.
让我们来看看代码。首先,您希望该课程是最终课程。在这种情况下,我使用final
关键字让用户知道它是最终的。然后您需要将构造函数设为私有以防止用户创建自己的 Foo。从构造函数抛出异常可防止用户使用反射来创建第二个 Foo。然后创建一个private static final Foo
字段来保存唯一的实例,并创建一个public static Foo getInstance()
方法来返回它。Java 规范确保只有在第一次使用类时才调用构造函数。
When you have a very large object or heavy construction code AND also have other accessible static methods or fields that might be used before an instance is needed, then and only then you need to use lazy initialization.
当您有一个非常大的对象或繁重的构造代码并且还有其他可访问的静态方法或字段时,这些方法或字段可能在需要实例之前使用,那么并且只有在那时您才需要使用延迟初始化。
You can use a private static class
to load the instance. The code would then look like:
您可以使用 aprivate static class
来加载实例。代码如下所示:
public final class Foo {
private static class FooLoader {
private static final Foo INSTANCE = new Foo();
}
private Foo() {
if (FooLoader.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return FooLoader.INSTANCE;
}
}
Since the line private static final Foo INSTANCE = new Foo();
is only executed when the class FooLoader is actually used, this takes care of the lazy instantiation, and is it guaranteed to be thread safe.
由于该行private static final Foo INSTANCE = new Foo();
仅在实际使用类 FooLoader 时执行,因此它负责延迟实例化,并保证线程安全。
When you also want to be able to serialize your object you need to make sure that deserialization won't create a copy.
当您还希望能够序列化您的对象时,您需要确保反序列化不会创建副本。
public final class Foo implements Serializable {
private static final long serialVersionUID = 1L;
private static class FooLoader {
private static final Foo INSTANCE = new Foo();
}
private Foo() {
if (FooLoader.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return FooLoader.INSTANCE;
}
@SuppressWarnings("unused")
private Foo readResolve() {
return FooLoader.INSTANCE;
}
}
The method readResolve()
will make sure the only instance will be returned, even when the object was serialized in a previous run of your program.
该方法readResolve()
将确保返回唯一的实例,即使对象在之前的程序运行中被序列化。