我正在学习基本的软件设计模式.
单例类的基本实现是这样写的:
public class MyObject{
private volatile static MyObject obj;
private MyObject(){/*Do some heavy stuff here*/}
public static synchronized MyObject getInstance(){
if(obj==null)
obj=new MyObject();
return obj;
}
}
Run Code Online (Sandbox Code Playgroud)
但是因为我不知道调用同步方法可能很重.
我回来的时候我红了一本书介绍了Singleton类的这种实现:
public class MyObjectHolder {
private volatile static Supplier<MyObject> myObjectSupplier = () -> createMyObj();
//myObjectSupplier is changed on the first 'get()' call
public static MyObject getMyObject(){
return myObjectSupplier.get();
}
private static synchronized MyObject createMyObj(){
class MyObjectFactory implements Supplier<MyObject> {
private final MyObject clockTimer = new MyObject();
public MyObject get() { return clockTimer; }
}
if(!MyObjectFactory.class.isInstance(myObjectSupplier)) {
myObjectSupplier = new MyObjectFactory();
}
return myObjectSupplier.get();
}
public static class MyObject{
private MyObject(){
/*Do some heavy stuff here*/
}
public void someMethod(){
/* ... */
}
}
}
...
{
/*In main MyObject instantiation*/
MyObjectHolder.MyObject obj = MyObjectHolder.getMyObject();
}
Run Code Online (Sandbox Code Playgroud)
现在,在第一次调用'createMyObj()'之后,已经完成了同步方法调用没有沉重负担,如果没有检查则不是.
你觉得这种实现有什么问题吗?
PS.MyObject不一定是MyObjectHold的内部类,但我认为它看起来不错.
[更新]另一种称为Initialization on Demand Holder的解决方案:
public class SingletonObject {
private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger();
private static final AtomicInteger INVOKE_COUNT = new AtomicInteger();
private static final class LazyHolder {
private static final SingletonObject INSTANCE = new SingletonObject();
}
private SingletonObject() {
System.out.println("new SingletonObject");
INSTANCE_COUNT.getAndIncrement();
}
public static SingletonObject getInstance() {
INVOKE_COUNT.getAndIncrement();
return LazyHolder.INSTANCE;
}
public static int getInstanceCount() {
return INSTANCE_COUNT.get();
}
public static int getInvokeCount() {
return INVOKE_COUNT.get();
}
}
Run Code Online (Sandbox Code Playgroud)
证明它是线程安全的:
public static void main(String[] args) throws Exception {
int n = 1000;
List<Callable<SingletonObject>> invokers = new ArrayList<>();
for (int i = 0; i < n; i++) {
invokers.add(SingletonObject::getInstance);
}
ExecutorService es = Executors.newFixedThreadPool(n);
es.invokeAll(invokers);
es.shutdown();
System.out.println("Number of Instances = " + SingletonObject.getInstanceCount());
System.out.println("Number of Invokes = " + SingletonObject.getInvokeCount());
}
Run Code Online (Sandbox Code Playgroud)
输出:
new SingletonObject
Number of Instances = 1
Number of Invokes = 1000
Run Code Online (Sandbox Code Playgroud)
编辑(在@Holger的评论之后):
使用的嵌套holder类是有些必要到懒洋洋初始化的SingletonObject.
public class SingletonObject {
private static final SingletonObject INSTANCE = new SingletonObject();
private SingletonObject() {
System.out.println("new SingletonObject");
}
public static SingletonObject getInstance() {
return INSTANCE;
}
public static void anotherStaticMethod() {
System.out.println("I don't need the SingletonObject Instance...");
}
}
Run Code Online (Sandbox Code Playgroud)
那么如果有人调用会发生什么anotherStaticMethod()呢?
new SingletonObject
I don't need the SingletonObject Instance...
Run Code Online (Sandbox Code Playgroud)
更新:
WIKIPEDIA的页面说:
习语的实现依赖于Java语言规范(JLS)指定的Java虚拟机(JVM)内的执行初始化阶段.当类
SingletonObject由JVM加载时,该类将进行初始化.由于该类没有任何静态变量来初始化,因此初始化完成很简单.LazyHolder在JVM确定LazyHolder必须执行之前,不会初始化其中的静态类定义.静态类LazyHolder仅getInstance在类上调用静态方法时执行SingletonObject,并且第一次发生这种情况时,JVM将加载并初始化LazyHolder类.LazyHolder类的初始化导致INSTANCE通过执行外部类的(私有)构造函数来初始化静态变量SingletonObject.由于JLS保证类初始化阶段是串行的,即非并发的,因此getInstance在加载和初始化期间静态方法不需要进一步的同步.并且由于初始化阶段将静态变量写入INSTANCE串行操作,因此所有后续的并发调用getInstance将返回相同的正确初始化,INSTANCE而不会产生任何额外的同步开销.这提供了一个高效的线程安全 "单例"缓存,没有同步开销; 基准测试表明它比甚至无竞争同步快得多.然而,该习语是单一特定的并且不可扩展到多个对象(例如,基于地图的高速缓存).
还要留意这一点.
| 归档时间: |
|
| 查看次数: |
358 次 |
| 最近记录: |