设计模式-单例模式

单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例

场景:重量级的对象,不需要多个实例,如线程池,数据库连接池

实现的关键点:私有化构造方法、提实现供公有的访问方式

常见实现方式:懒汉式(及其优化)、饿汉式、静态内部类、枚举

  • 懒汉式

延迟加载, 只有在真正使用的时候,才开始实例化。

优缺点:最简单的懒汉式有线程安全的问题, 这里直接给出double-check模式版本,虽然优化了线程安全的问题,但是逻辑稍显复杂了。

/**
 */
public class LazySingletonTest {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            LazySingleton instance = LazySingleton.getInstance();
            System.out.println(instance);

        });

        Thread thread2 = new Thread(() -> {
            LazySingleton instance = LazySingleton.getInstance();
            System.out.println(instance);

        });

        thread1.start();
        thread2.start();
    }
}


class LazySingleton {
    // volatile 禁止指令重排序
    private volatile static LazySingleton instance;
    private LazySingleton() {}

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

关键点分析

synchronized:保证线程安全;

双重非空判断:

        并不是每次进入getInstance方法都需要同步,首次非空判断后才加锁,缩小了加锁代码块的范围;

        第二次非空判断,防止代码被初始化两次;

volatile :编译器(JIT),CPU 有可能对指令进行重排序,导致使用到尚未初始化
的实例,导致非空判断失效,可以通过添加volatile 关键字进行修饰。被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量

  •  饿汉模式

饿汉式其实是一种比较形象的称谓。既然饿,那么在创建对象实例的时候就比较着急,饿了嘛,于是在装载类的时候就创建对象实例。

典型的空间换时间,当类装载的时候就会创建类的实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断,节省了运行时间。

优缺点:饿汉式天生线程安全(jvm类加载机制,后面具体分析),但是因为在装在的时候就直接创建了对象,大量创建使用的时候会导致内存的开销。

/**
 */
public class HungrySingletonTest {
    public static void main(String[] args) {
        HungrySingleton instance=HungrySingleton.getInstance();
        HungrySingleton instance1=HungrySingleton.getInstance();
        System.out.println(instance==instance1);
    }
}
class HungrySingleton{

    private static HungrySingleton instance = new HungrySingleton();

    private HungrySingleton(){

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

饿汉式本质上就是借助于jvm类加载机制,保证实例的唯一性(初始化过程只会执行一次)及线程安全(JVM以同步的形式来完成类加载的整个过程,这种思路可以在平时编码的过程中多借鉴)。
类加载过程:
1,加载二进制数据到内存中, 生成对应的Class数据结构,  
2,连接: a. 验证, b.准备(给类的静态成员变量赋默认值),c.解析
3,初始化: 给类的静态变量赋初值

  • 静态内部类

1).本质上是利用类的加载机制来保证线程安全
2).只有在实际使用的时候,才会触发类的初始化,所以也是懒加载的一
种形式。

package com.tuling.designpattern.singleton.v3;

import java.io.*;
import java.lang.reflect.InvocationTargetException;


public class InnerClassSingletonTest {
    public static void main(String[] args) {
      
        Thread thread1 = new Thread(() -> {
            InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
            System.out.println(instance1);
        });
        Thread thread2 =new Thread(()->{
            InnerClassSingleton instance1=InnerClassSingleton.getInstance();
            System.out.println(instance1);
        });

        thread1.start();
        thread2.start();




    }
}

class InnerClassSingleton implements Serializable {
    // private static final long serialVersionUID = 6922639953390195232L;
    // private  static final long serialVersionUID = 42L;
    public static String name="yyy";
    public static String name1="yyy";
    public static String name2="yyy";

    static {
        System.out.println( " InnerClassSingleton " ); //  1
    }

    private InnerClassSingleton() {

        if (SingletonHolder.instance != null) {
            throw new RuntimeException( " 不允许多个实例。" );
        }

    }

    public static InnerClassSingleton getInstance() {
        return SingletonHolder.instance;
    }

    Object readResolve() throws ObjectStreamException {
        return SingletonHolder.instance;
    }

    private static class SingletonHolder {
        private static InnerClassSingleton instance=new InnerClassSingleton();

        static {
            System.out.println( " SingletonHolder " );// 2
        }
    }

}

 类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。

 这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本

  • 枚举

参考文献

《JAVA与模式》之单例模式 - java_my_life - 博客园

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值