资讯专栏INFORMATION COLUMN

深入java单例模式

Aomine / 1976人阅读

摘要:单例是应用开发中一种设计模式,主要应用场景为当且仅当系统中只能保留一个对象时使用。本文提出中可以在生产环境中使用的单例设计模式。在的一书中给出了三种单例设计模式采用静态变量这种写法使用了私有的构造方法。

单例是应用开发中一种设计模式,主要应用场景为:当且仅当系统中只能保留一个对象时使用。本文提出4中可以在生产环境中使用的单例设计模式。推荐使用enum的方式。

应用场景

例如一下应用场景[1]
1、 Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗?

2、网站的浏览人数统计,一般也是采用单例模式实现,否则难以同步。

3、应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。

//todo
在joshua block 的《effective java second edition》 一书中给出了三种单例设计模式

1、采用静态变量:
public class TaskManager {
        public static final TaskManager INSTANCE = new TaskManager ();
        private TaskManager (){}
        //...
}

这种写法使用了私有的构造方法。来保证只能有一个实例,但是这种方法也有例外情况,因为,你可以通过反射来调用私有构造方法。这个时候你可以抛出异常。以下代码仅作为参考。

public class TaskManager {
    public static final TaskManager INSTANCE = new TaskManager();

    private TaskManager() {
        if (INSTANCE != null) {
            try {
                throw new Exception("An object already exists");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    //...
}
2、采用静态方法
public class TaskManager {
    private static final TaskManager INSTANCE = new TaskManager();

    private TaskManager() {}

    public static TaskManager getINSTANCE() {
        return INSTANCE;
    }

    //...
}
3、采用enum的方式

这种模式是目前最佳的,因为:
1、JVM会保证enum不能被反射并且构造器方法只执行一次。
2、此方法无偿提供了序列化机制,绝对防止反序列化时多次实例化。
3、运行时(compile-time )创建对象(懒加载) // todo 关于cmpile-time和run-time有时间我多带带写一篇文章。

enum是jdk5的特性,现在(2017)web应用普遍在jdk6、7、8,所以可以放心使用。

目前最佳的方式是使用接口的方式(解耦):

interface Resource {
    Object doSomething();
}

public enum SomeThing implements Resource {
    INSTANCE {
        @Override
        public Object doSomething() {
            return "I am a Singleton nstance";
        }
    };
}

class Demo {
    public static void main(String[] args) {
        System.out.println(SomeThing.INSTANCE.doSomething());
    }
}

或者不使用接口的形式

public enum SomeThing {
    INSTANCE;

    public void doSomething() {
        System.out.println("INSTANCE = " + INSTANCE);
    }

}

class Demo {
    public static void main(String[] args) {
        SomeThing.INSTANCE.doSomething();
    }
}

也有人用其他的方式,我对这种方法持强烈反对,具体可以参考文献4,以下代码仅做参考

class Resource {
}

public enum SomeThing {
    INSTANCE;
    private Resource instance;

    SomeThing() {
        instance = new Resource();
    }

    public Resource getInstance() {
        return instance;
    }
}

class Demo {
    public static void main(String[] args) {
        System.out.println(SomeThing.INSTANCE.getInstance());
    }
}

在其他文章中有提到“懒汉”、“恶汉”的名词,其实懒汉主要就是"懒"加载[注:指在使用时装载,不使用时不进行装载]

有人提出这种懒汉设计

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}

很显然这种设计线程不安全,一般不会使用。
有人又提出了懒汉改进的方法,使其线程安全。

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}  

这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,因为是重量级锁,效率很低。

于是有人提出了双重校验锁机制,这个用的也比较多。

下面代码就是用double checked locking 方法实现的单例,这里的getInstance()方法要检查两次,确保是否实例INSTANCE是否为null或者已经实例化了,这也是为什么叫double checked locking 模式。

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private volatile DoubleCheckedLockingSingleton INSTANCE;
 
     private DoubleCheckedLockingSingleton(){}
 
     public DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

参考文献:
[1] Jason Cai, 设计模式之——单例模式(Singleton)的常见应用场景
[2] cantellow, 单例模式的七种写法
[3] Javarevisited, 单例模式中为什么用枚举更好
[4] natsumi, Java枚举实现单例模式
[5] zejian_,深入理解Java枚举类型(enum)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70238.html

相关文章

  • 深入理解单例模式

    摘要:总结我们主要介绍到了以下几种方式实现单例模式饿汉方式线程安全懒汉式非线程安全和关键字线程安全版本懒汉式双重检查加锁版本枚举方式参考设计模式中文版第二版设计模式深入理解单例模式我是一个以架构师为年之内目标的小小白。 初遇设计模式在上个寒假,当时把每个设计模式过了一遍,对设计模式有了一个最初级的了解。这个学期借了几本设计模式的书籍看,听了老师的设计模式课,对设计模式算是有个更进一步的认识。...

    FuisonDesign 评论0 收藏0
  • 求职准备 - 收藏集 - 掘金

    摘要:一基础接口的意义百度规范扩展回调抽象类的意义想不想通过一线互联网公司面试文档整理为电子书掘金简介谷歌求职记我花了八个月准备谷歌面试掘金原文链接翻译者 【面试宝典】从对象深入分析 Java 中实例变量和类变量的区别 - 掘金原创文章,转载请务必保留原出处为:http://www.54tianzhisheng.cn/... , 欢迎访问我的站点,阅读更多有深度的文章。 实例变量 和 类变量...

    cuieney 评论0 收藏0
  • (CZ深入浅出Java基础)设计模式笔记

    摘要:在设计模式中,所有的设计模式都遵循这一原则。其实就是说在应用程序中,所有的类如果使用或依赖于其他的类,则应该依赖这些其他类的抽象类,而不是这些其他类的具体类。使用设计模式是为了可重用代码让代码更容易被他人理解保证代码可靠性。 这是刘意老师的JAVA基础教程的笔记讲的贼好,附上传送门 传智风清扬-超全面的Java基础 一、面向对象思想设计原则 1.单一职责原则 其实就是开发人员经常说的高...

    李昌杰 评论0 收藏0
  • Java 双重加锁单例java 内存重排序特性

    摘要:关于对于重排序的讲解,强烈推荐阅读程晓明写的深入理解内存模型二重排序。语义语义单线程下,为了优化可以对操作进行重排序。编译器和处理器为单个线程实现了语义,但对于多线程并不实现语义。双重加载的单例模式分析即双重检查加锁。 版权声明:本文由吴仙杰创作整理,转载请注明出处:https://segmentfault.com/a/1190000009231182 1. 引言 在开始分析双重加锁单...

    HackerShell 评论0 收藏0
  • Java 总结

    摘要:中的详解必修个多线程问题总结个多线程问题总结有哪些源代码看了后让你收获很多,代码思维和能力有较大的提升有哪些源代码看了后让你收获很多,代码思维和能力有较大的提升开源的运行原理从虚拟机工作流程看运行原理。 自己实现集合框架 (三): 单链表的实现 自己实现集合框架 (三): 单链表的实现 基于 POI 封装 ExcelUtil 精简的 Excel 导入导出 由于 poi 本身只是针对于 ...

    caspar 评论0 收藏0

发表评论

0条评论

Aomine

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<