Java设计模式—单例模式

2023-05-12 15:51:13 浏览数 (2)

Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点。

核心知识点如下:

(1) 将采用单例设计模式的类的构造方法私有化(采用private修饰)。

(2) 在其内部产生该类的实例化对象,并将其封装成private static类型。

(3) 定义一个静态方法返回该类的实例。

一:饿汉模式

优点是:写起来比较简单,而且不存在多线程同步问题,避免了synchronized所造成的性能问题; 缺点是:当类SingletonTest被加载的时候,会初始化static的instance,静态变量被创建并分配内存空间,从这以后,这个static的instance对象便一直占着这段内存(即便你还没有用到这个实例),当类被卸载时,静态变量被摧毁,并释放所占有的内存,因此在某些特定条件下会耗费内存

代码语言:javascript复制
package cn.design.singleton;

/**
 * 饿汉模式
 * 优点是:写起来比较简单,而且不存在多线程同步问题,避免了synchronized所造成的性能问题;
 * 缺点是:当类SingletonTest被加载的时候,会初始化static的instance,静态变量被创建并分配内存空间
 * 从这以后,这个static的instance对象便一直占着这段内存(即便你还没有用到这个实例),当类被卸载时,静态变量被摧毁,并释放所占有的内存,因此在某些特定条件下会耗费内存。
 * @author 翎野君
 *
 */
public class SingletonTest {
    
    //将构造方法私有化,使得外部不可以访问
    //只可以从定义的getInstance方法获取实例化对象
    //防止外部通过new SingleTonTest()实例化对象
    private SingletonTest(){
        
    }
    /**
     * instance外部不可以直接访问,随着类加载而加载
     * static静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配
     * final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
     */
    private static final SingletonTest instance=new SingletonTest();
    //静态方法,不随对象的不同而改变
    //返回上面定义的instance对象
    public static SingletonTest getInstance(){
        return instance;
    }
    public static void main(String[] args) {
        //在当前类下是可以访问的但是其他类不可以访问private变量
        SingletonTest st=new SingletonTest();
        //其他类中这样获取
        SingletonTest st1=SingletonTest.getInstance();
    }
}

二:饱汉模式

优点是:写起来比较简单,当类SingletonTest被加载的时候,静态变量static的instance未被创建并分配内存空间,当getInstance方法第一次被调用时,初始化instance变量,并分配内存,因此在某些特定条件下会节约了内存; 缺点是:并发环境下很可能出现多个SingletonTest实例。

代码语言:javascript复制
package cn.design.singleton;

public class SingletonTest1 {
    
    //将构造方法私有化防止外部通过new SingleTest1()获取对象
    private SingletonTest1(){
        
    }
    //饱汉模式就是吃饱了,不着急等初始化对象的时候在获取一个唯一实例
    //没有加final关键字,如果加上的话当即就要赋值
    //而饱汉模式要求动态调用的时候创建唯一实例
    private static SingletonTest1 instance;
    //定义一个静态方法等待调用的时候在对其进行对象初始化(多线程时无法保证只有一个对象)
    public static SingletonTest1 getInstance(){
        if(instance==null){
            instance=new SingletonTest1();
        }
        return instance;
    }
    
    public static void main(String[] args) {
        SingletonTest1 st=SingletonTest1.getInstance();
        System.out.println(st);
    }
}

三:饱汉模式的优化

优点是:使用synchronized关键字避免多线程访问时,出现多个SingletonTest实例。 缺点是:同步方法频繁调用时,效率略低。

代码语言:javascript复制
package cn.design.singleton;

/**
 * 优化饱汉模式
 * 优点:加锁防止多线程访问时出现多个实例的问题
 * 缺点:同步方法频繁调用时,效率略低。
 * @author 翎野君
 *
 */
public class SingletonTest2 {
    
    private SingletonTest2(){
        
    }
    private static SingletonTest2 instance;
    //定义一个静态方法,调用时进行初始化
    //加上一把锁synchronized之后防止出现多线程并发调用时出现多个实例的问题
    public synchronized static SingletonTest2 getInstance(){
        if(instance==null){
            instance=new SingletonTest2();
        }
        return instance;
    }
}

四:最优方案(不考虑反射的情况)

方法四为单例模式的最佳实现。内存占用地,效率高,线程安全,多线程操作原子性。

代码语言:javascript复制
package cn.design.singleton;

public class SingletonTest3 {

    private SingletonTest3(){
        
    }
    //使用volatile保证了多线程访问时instance变量的可见性,
    //避免了instance初始化时其他变量属性还没赋值完时,被另外线程调用
    //使用volatile保证了多线程访问时instance变量的可见性,避免了instance初始化时其他变量属性还没赋值完时,被另外线程调用
    private static volatile SingletonTest3 instance;
    
    public static SingletonTest3 getInstance(){
        if(instance==null){
            ////同步代码块(对象未初始化时,使用同步代码块,保证多线程访问时对象在第一次创建后,不再重复被创建)
            synchronized (SingletonTest3.class) {
                System.out.println(SingletonTest3.class);
                instance=new SingletonTest3();
            }
        }
        return instance;
    }
    public static void main(String[] args) {
        SingletonTest3 st=SingletonTest3.getInstance();
        
    }
}

0 人点赞