• 设计模式——单例模式


    知识点:单例模式——只有一个实例的类

    什么是设计模式:设计模式是在大量实践中总结和理论化之后优选的代码结构,编程风格,以及解决问题的思考方式。

    什么是单例模式:采取一定的方法保证,整个软件系统中,一个类只能存在一个对象实例

    具体实现

     在这个类的内部需要实现

    (1)将类的构造方法私有化,使得在在类的外部不能通过new创建该类的实例化对象

    (2)在类的内部实例化唯一的对象,用private static修饰

    (3)定义一个静态方法,供类的外部调用唯一的实例对象

      实现一 饿汉模式:在类加载时,对象就已经创建

    //饿汉式
    public class Singleton {

    //私有化构造器,这样外部的类无法调用构造方法
    private Singleton(){}

    public static String name;

    //内部实例化类的实例
    private static Singleton instance=new Singleton();

    //私有化对象,类通过公共方法调用
    public static Singleton getInstance(){
    return instance;
    }
    }

    补充
    当Singleton类被加载时,会初始化static修饰的instance,静态变量被创建并且在方法区(静态区)分配内存,Singleton对象存放在堆中,静态变量存放对象的地址,之后实例对象会一直占着这段内存,当类被卸载,

    静态变量才被摧毁,释放内存

    另一种静态代码块机制的写法
    //饿汉式(静态代码块)
    public class HungrySingleton1 {
    //1.私有化构造器
    private HungrySingleton1(){}

    //2.声明实例变量
    private static final HungrySingleton1 instance;

    //3.静态代码块中实例化
    static {
    instance=new HungrySingleton1();
    }
    //4.公共方法获得实例
    public static HungrySingleton1 getInstance(){
    return instance;
    }
    }
    测试:
    //饿汉式
    @Test
    public void testHungrySingleton(){
    ExecutorService executorService= Executors.newCachedThreadPool();
    for (int i=0;i<10;i++){
    executorService.execute(new Runnable(){
    @Override
    public void run() {
    HungrySingleton1 instance=HungrySingleton1.getInstance();
    System.out.println(Thread.currentThread().getName()+":"+instance);
    }
    });

    }
    }
    结果:


    实现二 懒汉模式:调用get()方法时,实例才被创建
    //懒汉式(线程安全问题)
    public class Singleton {

    //私有化构造器
    private Singleton(){}

    //内部实例化类的实例
    private static Singleton instance;

    //私有化对象,类通过公共方法调用(创建,返回实例)
    public static Singleton getInstance(){
    if(instance==null) {
    instance=new Singleton();
    }
    return instance;
    }
    }

        补充当Singleton类被加载时,静态变量instance其实在方法区中已经创建并且分配内存,当第一次调用getInstance方法,初始化instance,将堆中对象实例内存的地址赋值给变量instance

    另外在多线程的情况下,这种实现方式是线程不安全的

    实现三 线程安全的"懒汉模式":解决线程问题,使用同步机制,两种实现方式,同步代码块和同步方法两种

    (1)同步方法 (在方法上加synchronized修饰)
    //懒汉式(线程安全的)
    public class Singleton {

    //私有化构造器
    private Singleton(){}

    //内部实例化类的实例
    private static Singleton instance;
       //私有化对象,类通过公共方法调用(创建,返回实例)
    //方法上加同步锁
    public static synchronized Singleton getInstance(){
    if(instance==null) {
    instance=new Singleton();
    }
    return instance;
    }

    }
    (1)同步代码块 (在共享资源的代码外面,添加同步锁)
    
    
    //懒汉式(线程安全的)
    public class Singleton {

    //私有化构造器
    private Singleton(){}

    //内部实例化类的实例
    private static Singleton instance;

    public static Singleton getInstance(){
    //对于一般的方法内,使用同步代码块,可以考虑使用this
    //对于静态的方法,使用当前类本身充当锁
    synchronized(Singleton.class){
    if(instance==null) {
    instance=new Singleton();
    }
    }
    return instance;
    }
    }
     
     补充同步方法和同步代码块,保证了线程安全,但是多线程的情况下,效率不高,需考虑最佳的实现方案

    实现四 双重检查锁懒汉式:在同步代码块之前判断,单例是否实例化,如果没有实例化,那么第一个线程创建实例,之后的线程进入代码块之前,因为已经实例化,
    所以不会执行同步代码块,既保证了线程安全,又提高了效率

    //懒汉式(最佳)
    public class Singleton {

    //私有化构造器
    private Singleton(){}

    //内部实例化类的实例
    private static Singleton instance;

    public static Singleton getInstance(){

    //第一次检查instance是否被实例化,如果没有线程实例化,则进入if代码块
    if(instance==null) {
    //对于一般的方法内,使用同步代码块,可以考虑使用this
    //对于静态的方法,使用当前类本身充当锁
    synchronized (Singleton.class) {
    if (instance == null) {
    instance = new Singleton();
    }
    }

    }
    return instance;
    }
    }
    
    
    上面的几种单例模式比较常见,补充一下其他的单例模式(静态内部类懒汉式和枚举单例)
    静态内部类懒汉式

    双重检查锁懒汉式由于使用了synchronized,锁机制,会带来一些性能问题
    静态内部类懒汉式既可以解决饿汉式的内存浪费问题,也可以解决synchronized带来的性能问题

    //静态内部类懒汉式
    public class StaticLazyInnerClassSingleton {

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

    //公共方法调用,创建对象,并返回
    public static final StaticLazyInnerClassSingleton getInstance(){
    return InnerClass.instance;
    }

    private static class InnerClass{
    //final(保证不能够被修改)
    private static final StaticLazyInnerClassSingleton instance=new StaticLazyInnerClassSingleton();
    }
    }

    测试:
    //静态内部类懒汉式
    @Test
    public void testStaticInnerClassSingleton(){

    ExecutorService executorService= Executors.newCachedThreadPool();

    for (int i=0;i<10;i++){
    executorService.execute(new Runnable(){
    @Override
    public void run() {
    StaticLazyInnerClassSingleton instance=StaticLazyInnerClassSingleton.getInstance();
    System.out.println(Thread.currentThread().getName()+":"+instance);
    }
    });

    }
    }
    结果:

    静态内部类懒汉式原理
    类的加载初始化顺序:
    1.当类不被调用时,类的的静态内部类不会进行初始化,避免了内存的浪费
    2.当调用 getInstance()方法时,会先初始化静态内部类,因为静态内部类中的成员变量是final,所以即便是多线程的情况下,其成员变量是不会被修改的,因此解决了添加sychronized带来的性能问题

    枚举单例
    
    
    //枚举单例
    public enum EnumSingleton {
    INSTANCE;
    private Object instance;
    EnumSingleton(){
    instance =new EnumResource();
    }
    public Object getInstance(){
    return instance;
    }

    }
    class EnumResource{

    }
    测试
    //枚举单例
    @Test
    public void testEnumSingleton(){
    ExecutorService executorService= Executors.newCachedThreadPool();
    for (int i=0;i<10;i++){
    executorService.execute(new Runnable(){
    @Override
    public void run() {
    EnumSingleton instance=EnumSingleton.INSTANCE;
    System.out.println(Thread.currentThread().getName()+":"+instance.getInstance());
    }
    });

    }
    }
    结果

     关于单例模式还有静态内部类懒汉式,以及枚举单例等 参考 https://www.cnblogs.com/eamonzzz/p/11633482.html

    参考:https://www.cnblogs.com/binaway/p/8889184.html

  • 相关阅读:
    洛谷——P2018 消息传递
    洛谷——P2827 蚯蚓
    洛谷——P1120 小木棍 [数据加强版]
    洛谷——P1168 中位数
    洛谷——P1850 换教室
    Kali-linux使用Metasploit基础
    Kali-linux使用Metasploitable操作系统
    Kali-linux使用OpenVAS
    Kali-linux使用Nessus
    Kali-linux绘制网络结构图
  • 原文地址:https://www.cnblogs.com/shuaifing/p/10578661.html
Copyright © 2020-2023  润新知