设计模式之单例模式

设计模式之单例模式

对于单例模式,就是在一个软件系统中该类只允许创建一个对象或者说类的实例,该类负责创建自己的对象,同时确保只有单个对象被创建。那这个类就是一个单例类,这种设计模式叫做单例设计模式,简称单例模式。

单例模式的应用场景:

在JDK中的应用:

在JDK中java.lang.Runtime就是经典的单例模式(饿汉式)

image-20210824200212151

单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象)、但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)

如何实现一个单例

八种方法

实现一个单例需要关注一下几点:

  • 构造函数需要是private修饰的,可以避免外部通过new创建实例。
  • 创建对象时的线程安全问题。
  • 是否支持懒加载。
  • getinstance()方法的性能是否高。

饿汉式(静态变量)

//饿汉式(静态变量)
public class singleton01 {
    public static void main(String[] args) {
        single single = designpattern.single.getInstance();
        single single2 = designpattern.single.getInstance();
        System.out.println(single == single2);
        System.out.println(single.hashCode());
        System.out.println(single2.hashCode());


    }
}

class single {
    private static final single instance = new single();

    private single() {
    }

    public static single getInstance() {
        return instance;
    }
}

饿汉式的实现,在类加载的时候,instance静态实例就已经被创建并初始化了,因为类加载的时候是线程安全的,所以该方法是线程安全的,不过饿汉式的实现方式不支持懒加载,所谓懒加载就是在程序系统启动之后,用到了某个实例再创建这个实例。如果从始至终从未使用过这个实例,就是造成内存的浪费。

该方法可用,但可能会造成内存浪费。

饿汉式(静态代码块)

//饿汉式(静态代码块)
public class singleton02 {
    public static void main(String[] args) {
        single02 single = single02.getInstance();
        single02 single2 = single02.getInstance();
        System.out.println(single == single2);
        System.out.println(single.hashCode());
        System.out.println(single2.hashCode());


    }
}

class single02 {
    private static single02 instance;

    private single02() {
    }

    static {
        instance = new single02();
    }

    public static single02 getInstance() {
        return instance;
    }
}

这种方式将类实例化的过程放在了静态代码块中,在类加载的时候就执行静态代码块中的代码。

该方法可用,但可能会造成内存浪费。

懒汉式(线程不安全)

public class singleton03 {
    public static void main(String[] args) {
        single03 single = single03.getInstance();
        single03 single2 = single03.getInstance();
        System.out.println(single == single2);
        System.out.println(single.hashCode());
        System.out.println(single2.hashCode());


    }
}

class single03 {
    private static single03 instance;

    private single03() {
    }

    public static single03 getInstance() {
        if (instance == null) {
            instance = new single03();
        }
        return instance;
    }
}

懒汉式使用了懒加载,但是这种方式只能在单线程下使用,多线程下是不安全的,可能会创建多个实例对象。该方法不推荐使用。

懒汉式(线程安全,同步方法)

public class singleton04 {
    public static void main(String[] args) {
        single04 single = single04.getInstance();
        single04 single2 = single04.getInstance();
        System.out.println(single == single2);
        System.out.println(single.hashCode());
        System.out.println(single2.hashCode());
    }
}

class single04 {
    private static single04 instance;

    private single04() {
    }

    public static synchronized single04 getInstance() {
        if (instance == null) {
            instance = new single04();
        }
        return instance;
    }
}

使用synchronized将getInstance()方法标记为同步方法,是该方法在多线程下线程安全,但是

这样会是程序的性能降低,多线程情况下,虽然安全了,但是效率降低,当对象实例化之后,仍然需要加锁,后面的线程想要获得对象直接return就可以了,因此该方法不推荐使用。

懒汉式(线程安全,同步代码块)

public class singleton05 {
    public static void main(String[] args) {
        single05 single = single05.getInstance();
        single05 single2 = single05.getInstance();
        System.out.println(single == single2);
        System.out.println(single.hashCode());
        System.out.println(single2.hashCode());
    }
}

class single05 {
    private static single05 instance;

    private single05() {
    }

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

}

多线程下仍然可能会创建多个实例

双重检锁(DCL,Double Check Lock)

public class singleton06 {
    public static void main(String[] args) {
        single06 single = single06.getInstance();
        single06 single2 = single06.getInstance();
        System.out.println(single == single2);
        System.out.println(single.hashCode());
        System.out.println(single2.hashCode());
    }
}

class single06 {
    private static single06 instance;

    private single06() {
    }

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

}

在getInstance()方法中使用到了两个if判断语句,这样是为了避免多线程下因为线程切换导致创建多个实例的情况。

线程安全,延迟加载且效率较高。

静态内部类

public class singleton06 {
    public static void main(String[] args) {
        single06 single = single06.getInstance();
        single06 single2 = single06.getInstance();
        System.out.println(single == single2);
        System.out.println(single.hashCode());
        System.out.println(single2.hashCode());
    }
}

class single06 {


    private single06() {
    }

    private static class singleholder {
        private static final single06 INSTANCE = new single06();
    }

    public static single06 getInstance() {

        return singleholder.INSTANCE;
    }

}

采用类加载的机制类保证初始化实例时只有一个线程。

静态内部类在singleton类加载时不会实例化,当调用getInstance()方法时,因为需要返回内部类的变量,所以需要对内部类进行加载,从而完成实例化。

推荐使用该方法。

枚举类

public class singleton08 {
    public static void main(String[] args) {
        single08 single = single08.INSTANCE;
        single08 single2 = single08.INSTANCE;

        System.out.println(single == single2);

    }
}

enum single08 {
    INSTANCE;

}

使用枚举类可以避免多线程同步问题,还可以防止反序列化重新创建新的对象。

推荐使用。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值