java中你确定用对单例了吗?

作为程序猿这种特殊物种来说,都掌握了一种特殊能力就是编程思想,逻辑比较谨慎,但是有时候总会忽略到一些细节,比如我,一直以来总觉得Singleton是设计模式里最简单的,不用太在意,然而就是因为这种不在意在开发中吃亏了.真的too young to simple.

好不扯淡了,直入主题.

在代码的世界里发现有各种写法的单例,有人说单例有5种,6种,7种…

对于单例的分类这点必须规范下,首先这么多种的分类是根据什么来定义的,基准是什么?否则怎么会有那么多写法.

因此归纳下来,从延迟加载执行效率的角度出发主要也就分为两种,饿汉顾名思义就是执行效率高,但缺乏延时加载,其他写法差不多都是懒汉式的一个拓展,或者优化而演化出来的,下面请看代码.

开发中常用的单例-饿汉式

public class SingletonDemo1 {

    private static final SingletonDemo1 s1 = new SingletonDemo1();

    public static SingletonDemo1 getInstance() {
        return s1;
    }

    private SingletonDemo1() {
    }
}

没错上面这块代码叫做单例-饿汉式,饿汉式一直以效率高而闻名于单例界,因此咋们开发中常用的单例模式也会选择他,简单而好用.

>

开发评价: ★★★★☆

延时加载: ★☆☆☆☆

执行效率: ★★★★★

耗时的蜗牛-懒汉式

public class SingletonDemo2 {
    private static SingletonDemo2 s1;

    public static synchronized SingletonDemo2 getInstance() {
        if (s1 == null) {
            s1 = new SingletonDemo2();
        }
        return s1;
    }

    private SingletonDemo2() {
    }
}

hello world这个世界里都知道这种单例基本不会去用,在

double check双重检查锁-饿汉式

这可以说是上面饿汉式的一个缩影,为什么这么说,因为他并不完美,仍然有bug.

public class SingletonDemo3 {
    private static SingletonDemo3 s1;

    public static SingletonDemo3 getInstance() {
        if (s1 == null) {
        //这里使用了临时变量
            SingletonDemo3 instance;
            synchronized (SingletonDemo3.class) {
                instance = s1;
                if (instance == null) {
                    synchronized (SingletonDemo3.class) {
                        if (instance == null) {
                            instance = new SingletonDemo3();
                        }
                    }
                    s1 = instance;
                }
            }
        }
        return s1;
    }
}

这个方式主要是通过if判断非null实例,提高了执行的效率,不必每次getInstace都要进行synchronize,只要第一次要同步,有没创建了不用.

但是为什么说这种写法有bug?这个问题主要是java的jvm层内部模型引起的.简单点说就是instance引用的对象有可能还没有完成初始化完就直接返回该实例过去,在jdk1.5后这个问题才得到了优化,这不多说,可以看看这篇博文讲得不错.

详情见

当然也有了一些解决方法

  • 使用volatile关键字解决双重检查锁定的bug,对于volatile关键字就是Java中提供的另一种解决可见性和有序性问题的方案.
public class SafeDoubleCheckedLocking {
//添加了volatile关键字
    private volatile static Instance instance;

    public static Instance getInstance() {
        if (instance == null) {
            synchronized (SafeDoubleCheckedLocking.class) {
                if (instance == null)
                    instance = new Instance();//instance为volatile,现在没问题了
            }
        }
        return instance;
    }
}

>

开发评价: ★★★☆☆

延时加载: ★★★☆☆

执行效率: ★★★☆☆

推荐使用的静态内部类-饿汉式

public class SingletonDemo4 {

    //通过静态内部类的方式来实例化对象
    private static class InnerSingleton {
        private static final SingletonDemo4 instance = new SingletonDemo4();
    }

    public static  SingletonDemo4 getInstance() {
        return InnerSingleton.instance;
    }

    private SingletonDemo4() {
    }
}

这周方式是利用了类加载的一些特性,在classloder的机制中,初始化instance时只有一个线程,而且这种方式还有一个好处就是起到了延时加载的效果,当SingletonDemo4被加载了,但是内部类InnerSingleton并不会被加载,因为InnerSingleton没有主动使用,只有通过调用getInstance方法时才会去加载InnerSingleton类,进而实例private static final SingletonDemo4 instance = new SingletonDemo4();

因此这种巧妙的方式比起双重检查锁来说,安全来又高效了些.

>

开发评价: ★★★★☆

延时加载: ★★★★☆

执行效率: ★★★★☆

推荐使用的枚举-饿汉式

public enum SingletonDemo5 {

    //枚举元素本身就是一个单例(名字可以随意定义);
    INSTANCE;

    //可以做一些单例的初始化操作
    public void singletonOperation() {
    }
}

这种方式其实很帅,但是在实际开发中很少人使用过,这点有点奇怪,首先enum本身就是一个单例,而且枚举还有一个特性,可以避免放序列化和反射破解单例问题,经理再也不用担心单例安全了,可能是1.5才有enum的原因吧,如果项目适合的话可以试下这种单例.

>

开发评价: ★★★★☆

延时加载: ★★★★☆

执行效率: ★★★★☆

总结一下:

对于下面的单例总的来说各有各的优点,至于开发中使用哪个可以根据你的业务需求来选择.

  • 饿汉

    • 标准饿汉 (安全防护方面 枚举单例更优于标准饿汉)

      线程安全,高效,不可以懒加载

    • 枚举单例

      线程安全,高效,不可以懒加载(天然避免反射与反序列化)

      ?

  • 懒汉 (效率方面 静态内部类更优于标准懒汉)
    • 标准懒汉

      线程安全,低效,可以懒加载

    • 双重检测(不推荐,有bug)

      线程安全,低效,可以懒加载

    • 静态内部类

      线程安全,低效,可以懒加载

      ?

时间: 2024-10-12 23:35:25

java中你确定用对单例了吗?的相关文章

JAVA之旅(六)——单例设计模式,继承extends,聚集关系,子父类变量关系,super,覆盖

JAVA之旅(六)--单例设计模式,继承extends,聚集关系,子父类变量关系,super,覆盖 java也越来越深入了,大家加油吧!咱们一步步来 一.单例设计模式 什么是设计模式? JAVA当中有23种设计模式,解决某一问题最有效的方法 单例设计模式 解决一个类在内存中只存在一个对象 想要保证对象唯一该怎么做> 1.为了避免其他程序过多建立该类对象,先禁止其他程序建立该类对象 2.还为了让其他程序访问到该类对象,只好在本类中自定义一个对象 3.为了方便其他程序对自定义对象的访问,可以对外提供

Unity3D中可中途释放的单例

Unity3D中可中途释放的单例 使用静态类,静态变量的坏处是从程序加载后就一直占用内存,想要释放比较麻烦,可是之前使用的单例,没有提供释放的方法,那是不是也同静态的一样直到程序结束菜释放?那单例的好处是什么? 所以此处在单例中加入了可释放的方法来方便释放单例. 用途是: 用此单例管理场景物体时,在不切换场景的前提下释放掉该单例以及挂在单例游戏物体下的子物体 using UnityEngine; public abstract class SingleBhv<T> : IMono where

JAVA学习第十课(单例设计模式)

杂谈: 如果一个类里的方法都是静态的,那么就没有必要创建对象,为了不让其他程序创建对象,就可以把当前类的构造函数私有化. class MAN { private MAN() { } } 文档注释:命令:javadoc 只能解析/** 开始至 */结束的内容:/* */则不行 路径设置问题: 当要运行,两个以上路径中的class文件时,路径都要设置,命令:classpath=.;c:\myhelp或者:classpath=.;%myhelp% 清除路径: set classpath= 设计模式:对

Effective java 第2版 - 笔记(01) 单例(Singleton)的枚举(enum)实现

直接上代码: 1 public enum Boss { 2 3 INSTANCE; 4 5 private String name; 6 7 public void doSomeThing() { 8 System.out.println(name + " is doing something now..."); 9 } 10 11 public String getName() { 12 return name; 13 } 14 15 public void setName(Stri

java笔记之静态修饰附和单例设计模式

 第六天笔记 静态修饰符static: 一.static修饰成员变量: static用来修饰成员变量叫静态成员变量,没有static修饰的成员变量叫非静态成员变量 静态成员的访问方式: (1)   用对象进行访问:对象名.变量名 (2)   通过类名来访问:类名.变量名; 使用注意点: (1)   只有需要数据共享才需要用static修饰 //公有属性        String name;        //这个变量就变成了一个共享数据 ,单独存放在一个地方        static Str

【Java】设计模型-五种单例模型

一. 什么是单例模式 只需要某个类同时保留一个对象,不希望有更多对象,此时,我们则应考虑单例模式的设计. 单例模式的主要作用是保证在Java程序中,某个类只有一个实例存在. 单例模式有很多好处,它能够避免实例对象的重复创建,不仅可以减少每次创建对象的时间开销,还可以节约内存空间: 能够避免由于操作多个实例导致的逻辑错误.如果一个对象有可能贯穿整个应用程序,而且起到了全局统一管理控制的作用,那么单例模式也许是一个值得考虑的选择. 二. 单例模式的特点 1. 单例模式只能有一个实例. 2. 单例类必

C#中的简单工厂和单例

下面首先来说说简单工厂 举个例子: 首先是父类 public abstract class Pizza { public abstract string Info(); } } 子类 public class CheesePizza:Pizza { public override string Info() { return "您好,奶酪比萨成功订购"; } } 下一个子类 public class PGPizza:Pizza { public override string Info

SpringMVC中的controller默认是单例的原因

http://lavasoft.blog.51cto.com/62575/1394669/ 1.性能 :单例不用每次new浪费资源时间. 2.不需要:一般controller中不会定义属性这样单例就不会被影响到.

java中23种设计模式之4-单例模式(singleton pattern)

作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 单例类只能有一个实例. 单例类必须自己创建自己的唯一实例. 单例类必须给所有其他对象提供这一实例. 一般是通过private 来描述构造函数,禁止从外部构造对象,通过getInstance来获取唯一的实例. class Singleton{ private static Singleton aSingleton=null; private static int indexSingleton=0; pr