栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

设计模式(2)-创建者模式之单例模式

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

设计模式(2)-创建者模式之单例模式

1.单例模式

 介绍

 单例模式的实现

饿汉式:类加载就会导致该单实例对象被创建。在类加载时,不管用不用,这个对象都会在内存中,会造成内存的浪费。

方式1:静态变量方式

package 创建者模式.单例模式;

//单例模式  饿汉式 静态成员变量
public class Singleton {
    //1.私有构造方法,单例模式允许有一个对象,外界无法访问就不能创建对象
    private Singleton(){}
    //2.在本类中创建本类对象,在静态方法中要返回对象,所以这个属性需要时静态的
    private static Singleton instance = new Singleton();
    //3.提供一个公共的访问该对象的方式让外界获取该对象
    //外界不能创建这个对象,没有对象就不能调用普通方法,所以要用类名.静态方法名的方式来调用方法,从而获取对象
    public static Singleton getInstance(){return instance;};
}
package 创建者模式.单例模式;

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

        Singleton instance1 = Singleton.getInstance();

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

 方式2:静态代码块方式

package 创建者模式.单例模式饿汉式静态代码块方式;

public class Singleton {
    //1.构造私有方法
    private Singleton(){};
    //2.Singleton对象
    private static Singleton instance;
    //3.为该对象赋值
    static {
        instance = new Singleton();
    }

    public static Singleton getInstace(){
        return instance;
    }
}
package 创建者模式.单例模式饿汉式静态代码块方式;

public class Client {
    public static void main(String[] args) {

        Singleton instance = Singleton.getInstace();

        Singleton instance1 = Singleton.getInstace();
        //判断是否做到单例
        System.out.println(instance1 == instance);//true
    }
}

方式3:枚举方式

枚举类型线程安全,并且只会加载一次

public enum Singleton {
    INSTANCE;
}
public class Client {
    public static void main(String[] args) {
        Singleton instance = Singleton.INSTANCE;
        Singleton instance1 = Singleton.INSTANCE;
        System.out.println(instance1 == instance);//true
    }
}

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

方式1:线程不安全

package 创建者模式.单例模式懒汉式线程不安全方式;


public class Singleton {
    private Singleton(){};


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

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

方式2:线程安全 

package 创建者模式.单例模式懒汉式线程不安全方式;


public class Singleton {
    private Singleton(){};


    //这里只是声明了一个类型的变量,没有进行赋值
    private static Singleton instance;
    //与方式1的区别就是加了关键字 synchronized
    public static synchronized Singleton getInstance(){
        
        if(instance == null){
            instance = new Singleton();
        }
            return instance;
    }
}

方式3:双重检查锁

对于方法2,由于获取了一次对象后,以后每次判断就会是false了,所以每次都不用执行if里面的语句,无论哪个线程在执行return时都会是安全的,只有在未确定对象时,线程才会不安全,但每次每个线程都要进入锁就会影响代码效率,所以要调整加锁时机。所以有了双重检查。

对synchronized用法不明白看synchronized 参数 及其含义_知我饭否-CSDN博客_synchronized参数

package 创建者模式.单例模式饿汉式双重检查锁;

public class Singleton {
    private Singleton(){};

    private static Singleton instance;

    public static Singleton getInstance(){
        //第一次判断,如果instance值不为null,不需要抢占锁,直接返回对象。
        if(instance == null){
            //锁的对象是当前类的字节码对象
            synchronized (Singleton.class){
                //第二次判断
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;

    }
}

在声明instance时,在static后面加上volatile 

方式4:静态内部类方式

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

第一次加载Singleton类时不会去初始化INSTANCE,只有第一次调用getInstance时,虚拟机加载SingleHolder并初始化INSTANCE,这样不仅能确保线程安全,也能保证Singleton类的唯一性。 

在类里面是保证不调用方法时不创建对象占用内存,因为调用时才会加载内部类。

public class Singleton {
    private Singleton(){

    }
    //声明静态内部类
    private static class SingletonHolder{
        //声明并初始化对象,final是为了防止外部对他修改
        private final static Singleton INSTANCE = new Singleton();
    }
    //提供公共访问方式
    public static Singleton getInstance(){
        return SingletonHolder.INSTANCE;
    }
}

小结:推荐使用双重检查锁和静态内部类这两种方式的单例模式。在不考虑浪费空间的条件下,也可以选择饿汉式的枚举方式。

存在的问题

如果想要破坏上面的单例模式(枚举方式除外)的话即可以创建多个对象有两种方式,序列化和反射。

问题的解决

因为还没有学序列化和反射所以先空着

JDK源码中单例模式的应用

Runtime类

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/732995.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号