首页 > 学院 > 开发设计 > 正文

单例模式贯通

2019-11-10 20:29:01
字体:
来源:转载
供稿:网友

单例设计模式:

单例概念: java中单例设计模式是一种常见的设计模式,单例设计模式的写法有好多种: 懒汉式单例饿汉式单例枚举单例(这种写法比较少见,但是值得讨论和使用)单例设计模式具备的特点: 单例类只能有一个实例单例类必须自己创建自己的唯一实例单例类必须给所有其他对象提供这一实例

懒汉式单例:

注意点: 类内部私有封装一个自己的引用构造器私有化提供获取该类内部私有封装的唯一方法

缺点:

懒汉式单例设计的实现没有考虑过线程安全问题,它是非线程安全的,在并发环境下,很可能会出现多个SingleTon实例。

实例:

非线程安全:

public class Person { //类内部封装自己的引用,该引用必须私有 PRivate static Person person = null; //构造器私有化 private Person(){ } //提供获取单例的唯一接口 public static Person getInstance() { if(person == null) { person = new Person(); } return person; }}

改造:

在获取方法中,加入同步机制:

synchronized修饰获取方法:

缺陷:

虽然线程安全了,但是每次都要进行同步,因此会影响性能

//提供获取单例的唯一接口public synchronized static Person getInstance() { if(person == null) { person =new Person(); } return person;}

双重检查机制:

改善:

做了两次判空操作,确保了只有第一次调用单例的时候才会做同步,也避免了同步的性能损耗

//提供获取单例的唯一接口public static Person getInstance() { if(person ==null) { synchronized (Person.class) { if(person == null) { person =new Person(); } } } return person;}

使用静态内部类并且加上final修饰的机制:

改善:

利用ClassLoader的机制类保证初始化单例对象的时候,只有一个线程,所以也是线程安全的,同时还没有性能的损耗。

public class Person{ //构造器私有化 private Person() { } //写一个静态内部类,用来提供单例对象 private static class LazyHolder { public static final Person SINGLEINSTANCE = new Person(); } //获取单例对象的方法 public static Person getInstance(){ return LazyHolder.SINGLEINSTANCE; }}

饿汉式单例:

因为饿汉式单例是在类创建的同时,就已经创建好了一个静态的对象供给系统使用,以后不再改变,所以是天生线程安全的

实例:

public class Person { //类内部封装自己的引用,该引用必须私有 private static Person person = new Person(); //构造器私有化 private Person(){ } //提供获取单例的唯一接口 public static Person getInstance() { return person; }}

饿汉式和懒汉式的区别:

从名字上区分: 饿汉: 类一旦加载,就把单例初始化完成,保证取单例的时候,单例是绝对存在的懒汉: 比较懒,只有当取单例的时候,才会去初始化这个单例对象线程安全: 饿汉式: 天生线程安全,可以直接不用担心多线程的安全问题懒汉式: 本身是非线程安全的,为了实现线程安全,需要额外做操作。

枚举单例:

前面介绍了懒汉式单例、饿汉式单例,最近在网上看到有大神提出可以使用枚举类型创建单例。优点: 我们知道,上述的这些不管是懒汉式、饿汉式,都逃不开一个问题:反射机制能够进行攻击,这样单例就失效了。因此如果想要对单例进行保护,就要使用枚举单例了。枚举类型天生就是线程安全的,也不需要去考虑线程安全问题。所以,看来看去还是枚举单例用起来比较高大上。

且看下面代码实例:

public enum Person {INSTANCE;Person() { //单例构造,默认私有}@Overridepublic String toString() { return super.toString();}}class test{public void go(){ //直接使用枚举类型调用单例 Person.INSTANCE.toString();}}
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表