设计模式-03-单例模式

一、单例模式简介

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

注意:

  • 1、单例类只能有一个实例。
  • 2、单例类必须自己创建自己的唯一实例。
  • 3、单例类必须给所有其他对象提供这一实例。

介绍

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决:一个全局使用的类频繁地创建与销毁。

何时使用:当您想控制实例数目,节省系统资源的时候。

如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:构造函数是私有的。

优点:

  • 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
  • 2、避免对资源的多重占用(比如写文件操作)。

缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

使用场景:

  • 1、要求生产唯一序列号。
  • 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
  • 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

二、单例模式的实现

单例设计模式分两种:

        饿汉式:类加载就会导致该单实例对象被创建

        懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会被创建

三、代码实现

1、饿汉式:静态成员变量
package SP_01_Hungry;

/**
 * @Author: {LZG}
 * @ClassName: SP_01_Hungry.Singleton
 * @Description: 饿汉式:静态成员变量
 * @Date: 2022/3/31 14:06
 **/
public class Singleton {

    //  1、私有构造法方法
    private Singleton() {
    }

    //  2、在本类中创建本类对象
    private  static Singleton instance=new Singleton();

    //  3、提供一个公共的方式访问,让外界获取该对象
    public static Singleton getInstance(){
        return instance;
    }
}

 测试

package SP_01_Hungry;

/**
 * @Author: {LZG}
 * @ClassName: Client
 * @Description: TODO
 * @Date: 2022/3/31 14:10
 **/
public class Client {

    public static void main(String[] args) {
        //  创建Singleton类的对象
        Singleton instance1=Singleton.getInstance();

        Singleton instance2=Singleton.getInstance();

        //  判断获取两到的两个是否是同一对象
        System.out.println(instance1==instance2);
    }
}

执行结果

2、饿汉式:静态代码块
package SP_02_Hungry2;

/**
 * @Author: {LZG}
 * @ClassName: Singleton
 * @Description: 饿汉式:静态代码块
 * @Date: 2022/3/31 14:32
 **/
public class Singleton {

    //  私有构造方法
    private Singleton(){}

    //  声明Singleton类型的变量
    private static Singleton instance;   //   null

    //  在静态代码块赋值
    static{
        instance=new Singleton();
    }

    //  对外提供该类对象的获取方法
    public static Singleton getInstance(){
        return instance;
    }
}

测试

package SP_02_Hungry2;


/**
 * @Author: {LZG}
 * @ClassName: Client
 * @Description: TODO
 * @Date: 2022/3/31 14:10
 **/
public class Client {

    public static void main(String[] args) {
        //  创建Singleton类的对象
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();

        //  判断获取两到的两个是否是同一对象
        System.out.println(instance1==instance2);
    }
}

运行结果

3、懒汉式:线程不安全

package SP_03_Lazy;

/**
 * @Author: {LZG}
 * @ClassName: Singleton
 * @Description: 懒汉式:线程不安全
 * @Date: 2022/3/31 14:48
 **/
public class Singleton {

    //  私有构造方法
    private Singleton(){}

    //  声明Singleton类型的变量instance
    private static Singleton instance;      //  只是声明一个该类型的变量,并没有进行赋值

    //  对外提供访问方式
    public static Singleton getInstance(){
        if(instance==null){
            //  线程1等待,线程2获取到了cpu的执行权,也会进入到判断里边
            instance=new Singleton();
        }
        return instance;
    }


}

测试

package SP_03_Lazy;

/**
 * @Author: {LZG}
 * @ClassName: Client
 * @Description: TODO
 * @Date: 2022/3/31 14:50
 **/
public class Client {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();

        System.out.println(instance1==instance2);
    }
}

 测试结果

 4、懒汉式:线程安全

package SP_04_Lazy;

/**
 * @Author: {LZG}
 * @ClassName: Singleton
 * @Description: 懒汉式:线程安全
 * @Date: 2022/3/31 14:55
 **/
public class Singleton {

    //  私有构造方法
    private Singleton(){}

    //  声明Singleton类型的变量instance
    private static Singleton instance;      //  只是声明一个该类型的变量,并没有进行赋值

    //  对外提供访问方式
    //  synchronized:同步锁 线程1等待  线程2获得cpu的执行权 但是线程1没有释放同步锁 所以线程2进不去
    public static synchronized Singleton getInstance(){
        if(instance==null){
            //  线程1等待,线程2获取到了cpu的执行权,也会进入到判断里边
            instance=new Singleton();
        }
        return instance;
    }

}

 测试

package SP_04_Lazy;

/**
 * @Author: {LZG}
 * @ClassName: Client
 * @Description: TODO
 * @Date: 2022/3/31 14:58
 **/
public class Client {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1==instance2);
    }
}

运行结果

5、懒汉式:双重检查锁

对于getInstance()方法来说,绝大部分的操作都是都操作,读操作是线程安全的,所以没必要让每个线程必须持有锁才能调用该方法,我们需要调整加锁机制。由此产生了新的模式:双重检查锁模式 

package SP_05_Lazy;

/**
 * @Author: {LZG}
 * @ClassName: Singleton
 * @Description: 懒汉式:双重检查锁
 * @Date: 2022/3/31 15:05
 **/
public class Singleton {
    //  私有构造方法
    private Singleton(){}

    //  声明Singleton类型的变量instance
    private static Singleton instance;      //  只是声明一个该类型的变量,并没有进行赋值

    //  对外提供访问方式
    public static  Singleton getInstance(){
        //  第一次判断。如果instance的值不为null,不需要抢占锁,直接返回对象
        if(instance==null){
            synchronized (Singleton.class){
                //  第二次判断
                if (instance==null){
                    instance=new Singleton();
                }
            }
        }
        return instance;
    }
    
}

上述代码解决了单例、性能、线程安全问题,看似完美无缺,其实存在在多线程的情况下可能会出现空指针问题,出现JVM在实例化对象的时候会进行优化和指令重排序操作。要解决上述空指针异常问题,只需要使用volatile关键字,volatile可以保证可见性和有序性。

package SP_05_Lazy;

/**
 * @Author: {LZG}
 * @ClassName: Singleton
 * @Description: 懒汉式:双重检查锁
 * @Date: 2022/3/31 15:05
 **/
public class Singleton {
    //  私有构造方法
    private Singleton(){}

    //  声明Singleton类型的变量instance
    private static volatile Singleton instance;      //  只是声明一个该类型的变量,并没有进行赋值

    //  对外提供访问方式
    public static  Singleton getInstance(){
        //  第一次判断。如果instance的值不为null,不需要抢占锁,直接返回对象
        if(instance==null){
            synchronized (Singleton.class){
                //  第二次判断
                if (instance==null){
                    instance=new Singleton();
                }
            }
        }
        return instance;
    }
    
}

6、懒汉式:静态内部类

静态内部类单例模式中实例由内部类创建,由于JVM在加载外部类的过程中,是不会加载静态内部类,只由内部的属性/方法被调用的时候才会被加载,并初始化其静态属性。静态属性由于被static修饰,保证只被实例化一次,并且严格保证实例化顺序

package SP_06_Lazy;

/**
 * @Author: {LZG}
 * @ClassName: Singleton
 * @Description: 懒汉式:静态内部类
 * @Date: 2022/3/31 15:20
 **/
public class Singleton {

    //  私有构造方法
    private Singleton(){}
    //  定义一个静态内部类
    private static class SingletonHolder{
        private static final Singleton INSTANCE=new Singleton();
    }
    //  提供公共的访问方式
    public static Singleton getInstance(){
        return SingletonHolder.INSTANCE;
    }

}

测试

package SP_06_Lazy;

/**
 * @Author: {LZG}
 * @ClassName: Client
 * @Description: TODO
 * @Date: 2022/3/31 15:25
 **/
public class Client {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1==instance2);
    }
}

运行结果

 7、枚举方式

枚举实现单例模式是极力推荐的单例模式,因为枚举是线程安全的,并且置喙状再一次,枚举的写法十分简单,而且枚举类型是所用单例实现中唯一不会被破坏的单例实现模式

package SP_07_Enum;

/**
 * @Author: {LZG}
 * @ClassName: Singleton
 * @Description: 枚举方式
 * @Date: 2022/3/31 15:20
 **/
public enum Singleton {
    INSTANCE;
}

测试

package SP_07_Enum;

/**
 * @Author: {LZG}
 * @ClassName: Client
 * @Description: TODO
 * @Date: 2022/3/31 15:37
 **/
public class Client {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        System.out.println(instance1==instance2);
    }
}

运行结果

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值