java设计模式之单例模式

本文介绍java23种设计模式之单例模式

概念:
     单例模式 是java中设计模式常见常用的一种
     本文主要讲单例模式中 懒汉式 饿汉式 登记式 枚举等等。。
特点:
     单例模式只能有一个实例
     单例模式必须自己创建自己的唯一实例
     单例模式给其他对象提供唯一的实例

一:懒汉式

/**
 *
 * 懒汉式-第一次调用时初始化
* */
public class Singleton {
    //私有化静态变量
    private static Singleton singleton = null;
    //私有化构造
    private Singleton() {

    }
    //提供唯一方法给外部调用
    public static Singleton getSingleton() {
        //没有就创建 有就直接返回
        if (singleton == null) {
             singleton = new Singleton();
        }
        return singleton;
    }
}

Singleton中私有化构造避免了实例在外部被实例化,从而提供了一个getSinglton()方法来进行调用和访问。
(事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。)
但是问题来了,以上的单例模式并没有考虑安全性问题,在多线程下是不安全的,并发环境下很可能产生多个singleton实例对象。
要实现线程安全的话可以通过以下三种方式实现,皆对于getSingletion()方法进行的改造。

第一种:      直接在getSingleton()方法上加上同步锁 synchronized

 //提供唯一方法给外部调用
    public static  synchronized Singleton getSingleton() {
        //没有就创建 有就直接返回
        if (singleton == null) {
             singleton = new Singleton();
        }
        return singleton;
    }

第二种:      双重校验锁

 //提供唯一方法给外部调用
    public static Singleton getSingleton() {
        //判断
        if (singleton == null) {
            //获取锁
            synchronized (Singleton.class) {
                //判断
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

第三种:      静态内部内

/**
 * 懒汉式-第一次调用时初始化
 */
public class Singleton {
    //类初始时直接初始化 依赖jvm实现单例
    public static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    //私有化构造
    private Singleton() {
    }

    //提供唯一方法给外部调用
    public static Singleton getSingleton() {
        return SingletonHolder.INSTANCE;
    }

}

推荐第三种 既实现了线程安全 又省去了多线程性能的消耗

二:饿汉式

/**
 * 饿汉式- 在类初始化就初始化实例
 */
public class Singleton {
    //类初始化直接初始化实例
    private static final Singleton singleton = new Singleton();
    //私有化构造
    private Singleton() {

    }
    //提供一个方法给外部访问
    public static Singleton getSingleton() {
        return singleton;
    }
}

饿汉式在类初始化的时候直接实例化了一个final静态对象来提供使用 是不可变的 所以线程自带安全的

三:登记式模式(忽略就行)


//类似Spring里面的方法,将类名注册,下次从里面直接获取。
public class Singleton3 {
    private static Map<String,Singleton3> map = new HashMap<String,Singleton3>();
    static{
        Singleton3 single = new Singleton3();
        map.put(single.getClass().getName(), single);
    }
    //保护的默认构造子
    protected Singleton3(){}
    //静态工厂方法,返还此类惟一的实例
    public static Singleton3 getInstance(String name) {
        if(name == null) {
            name = Singleton3.class.getName();
            System.out.println("name == null"+"--->name="+name);
        }
        if(map.get(name) == null) {
            try {
                map.put(name, (Singleton3) Class.forName(name).newInstance());
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return map.get(name);
    }
    //一个示意性的商业方法
    public String about() {    
        return "Hello, I am RegSingleton.";    
    }    
    public static void main(String[] args) {
        Singleton3 single3 = Singleton3.getInstance(null);
        System.out.println(single3.about());
    }
}

登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。

这里我对登记式单例标记了可忽略,我的理解来说,首先它用的比较少,另外其实内部实现还是用的饿汉式单例,因为其中的static方法块,它的单例在类被装载的时候就被实例化了。

四:枚举

/**
 * 枚举 -实现单例模式
* */
public enum EnumSingleton {
    //枚举元素本身就是单例
    UNIQUEINSTANCE,;

    //自己需要的操作
    public void getSomething() {
        System.out.println("UNIQUEINSTANCE = " + UNIQUEINSTANCE);
    }
}

枚举单例模式 线程安全,调用效率高,不能延时加载
饿汉式和懒汉式区别

小结:
从名字上来说,饿汉和懒汉,

饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,

而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

另外从以下两点再区分以下这两种方式:

1、线程安全:

饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,

懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的1、2、3,这三种实现在资源加载和性能方面有些区别。

2、资源加载和性能:

饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,

而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

至于1、2、3这三种实现又有些区别,

第1种,在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的,

第2种,在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗

第3种,利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗,所以一般我倾向于使用这一种。

内容大部分引用与
https://blog.csdn.net/jason0539/article/details/23297037?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162028664716780269851153%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=162028664716780269851153&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~hot_rank-1-23297037.pc_search_result_no_baidu_js&utm_term=java%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值