设计模式-单例

设计模式-单例

一、简介

单例模式,意思就是在一个Java虚拟机实例内,某个类只能存在一个实例对象。

二、应用场景列举

1.资源管理对象

如硬件资源管理(蓝牙管理、麦克风、摄像头等),又如连接资源管理(数据库连接、socketServer、socketClient等)。

2.上下文

如Java的运行时上下文,如springmvc的当前用户管理对象等 。

3.通用工具

如全局的运行log记录工具、操作日志记录工具,又如唯一ID生成工具等。

4.游戏中的NPC,大BOSS等。

三、实现形式

1.饿汉式

public class HungrySingle {
    private String from;

    public String getFrom() {
        return from;
    }

    private HungrySingle(String from) {
        this.from = from;
        System.out.println("--create--from:" + from);
    }

    private static final HungrySingle single = new HungrySingle("类加载时new");

    public static HungrySingle getInstance() {
        System.out.println("--getInstance--");
        return single;
    }

    public static void main(String[] args) throws Exception {
        //多线程跑
        for (int i = 0; i < 10; i++) {
            new Thread() {
                @Override
                public void run() {
                    HungrySingle instance = HungrySingle.getInstance();
                    System.out.println(instance.toString() + "---from:" + instance.getFrom());
                }
            }.start();
        }
        //反射创建
        Constructor<HungrySingle> declaredConstructor = HungrySingle.class.getDeclaredConstructor(String.class);
        declaredConstructor.setAccessible(Boolean.TRUE);
        HungrySingle instance2 = declaredConstructor.newInstance("反射");
        //打印hasCode
        System.out.println(instance2.toString() + "--from:" + instance2.getFrom());

    }
}
饿汉式特点:
  • 1.线程安全(jvm在加类该类时,对象就new出来了,因此根本不存在线程安全问题);
  • 2.可以通过反射破解,创建新的对象;
  • 3.因为jvm加载该类时,对象就创建了,并不是首次调用获取对象的方法时才去创建,因此不是延迟加载。
  • 4.获取和创建过程都没同步锁,不存在多线程下的同步开销问题

2.简单懒汉式单例

Java

public class SimpleLazySingle {
    private String from;

    public String getFrom() {
        return from;
    }

    private SimpleLazySingle(String from) {
        this.from=from;
        System.out.println("--create,from:"+from+"---");
    }


    private static SimpleLazySingle single = null;

    public static SimpleLazySingle getInstance(String from) {
        System.out.println("--getInstance---");
        if (single == null) {
            single = new SimpleLazySingle(from);
        }
        return single;
    }

    public static void main(String[] args) throws Exception {
        //多线程跑
        for (int i = 0; i < 10; i++) {
            new Thread(){
                @Override
                public void run() {
                    SimpleLazySingle instance = SimpleLazySingle.getInstance("多线程");
                    System.out.println(instance.toString()+"---from:"+instance.getFrom());
                }
            }.start();
        }
        //反射创建
        Constructor<SimpleLazySingle> declaredConstructor = SimpleLazySingle.class.getDeclaredConstructor(String.class);
        declaredConstructor.setAccessible(Boolean.TRUE);
        SimpleLazySingle instance2 = declaredConstructor.newInstance("反射");
        //打印hasCode
        System.out.println(instance2.toString()+"---from:"+instance2.getFrom());
    }
}
简单懒汉式特点:
  • 1.线程不安全,当多个线程同时调用getInstance方法时,可能会new出多个实例
  • 2.可以通过反射破解
  • 3.必需要调getInstance()方法时才会new一个实例对象,实现了延迟加载
  • 4.获取和创建过程都没同步锁,不存在多线程下的同步开销问题

3.同步方法懒汉式单例

Java

public class SimpleLazySingle {
    private String from;

    public String getFrom() {
        return from;
    }

    private SimpleLazySingle(String from) {
        this.from=from;
        System.out.println("--create,from:"+from+"---");
    }


    private static volatile SimpleLazySingle single = null;

    public synchronized static SimpleLazySingle getInstance(String from) {
        System.out.println("--getInstance---");
        if (single == null) {
            single = new SimpleLazySingle(from);
        }
        return single;
    }

    public static void main(String[] args) throws Exception {
        //多线程跑
        for (int i = 0; i < 10; i++) {
            new Thread(){
                @Override
                public void run() {
                    SimpleLazySingle instance = SimpleLazySingle.getInstance("多线程");
                    System.out.println(instance.toString()+"---from:"+instance.getFrom());
                }
            }.start();
        }
        //反射创建
        Constructor<SimpleLazySingle> declaredConstructor = SimpleLazySingle.class.getDeclaredConstructor(String.class);
        declaredConstructor.setAccessible(Boolean.TRUE);
        SimpleLazySingle instance2 = declaredConstructor.newInstance("反射");
        //打印hasCode
        System.out.println(instance2.toString()+"---from:"+instance2.getFrom());
    }
}
同步方法懒汉式特点:
  • 1.线程安全,通过同步方法,保障在多线程的情况下,不会创建多个实例
  • 2.可以通过反射破解
  • 3.必需要调getInstance()方法时才会new一个实例对象,实现了延迟加载
  • 4.getInstance()方法性能差,因为多线程的情况下,所有线程对该方法的调用都需要同步开销,都需要先获取锁。
  • 5.需加volatile,以防止多线程下因指令重排序引发的对象未初始化完成的问题

4.双重检查同步锁懒汉式单例

Java

public class DoubleCheckSyncLazySingle {
   private String from;

   public String getFrom() {
       return from;
   }

   private DoubleCheckSyncLazySingle(String from) {
       this.from = from;
       System.out.println("---create,from:" + from);
   }

   private static volatile DoubleCheckSyncLazySingle single = null;

   public static DoubleCheckSyncLazySingle getInstance(String from) {
       //当一批程同步调用该方法创建时,前面已获得线程锁的线程创建后对象后,
       //后面的线程再来时,该条件就不成立,直接返回对象,避免了所有线程都要去等待锁。
       if (single == null) {
           synchronized (DoubleCheckSyncLazySingle.class) {
               if (single == null) {
                   single = new DoubleCheckSyncLazySingle(from);
               }
           }
       }
       return single;
   }

   public static void main(String[] args) throws Exception {
       //多线程跑
       for (int i = 0; i < 10; i++) {
           new Thread() {
               @Override
               public void run() {
                   DoubleCheckSyncLazySingle instance = DoubleCheckSyncLazySingle.getInstance("多线程");
                   System.out.println(instance.toString() + "---from:" + instance.getFrom());
               }
           }.start();
       }
       //反射创建
       Constructor<DoubleCheckSyncLazySingle> declaredConstructor = DoubleCheckSyncLazySingle.class.getDeclaredConstructor(String.class);
       declaredConstructor.setAccessible(Boolean.TRUE);
       DoubleCheckSyncLazySingle instance2 = declaredConstructor.newInstance("反射");
       System.out.println(instance2.toString() + "---from:" + instance2.getFrom());
   }
}
双重检查同步锁懒汉式特点:
  • 1.线程安全,一个类的类对象在一个jvm实例中只能有一人,锁是唯一的。
  • 2.可以通过反射破解
  • 3.必需要调getInstance()方法时才会new一个实例对象,实现了延迟加载
  • 4.只在首次创建时存在多线程同步开销问题,一旦对象创建后就没有了同步开销。

5.静态内部类懒汉式单例

Java

public class StaticInnerClassLazySingle {
   private String from;

   public String getFrom() {
       return from;
   }

   private StaticInnerClassLazySingle(String from) {
       this.from = from;
       System.out.println("--create-from:" + from);
   }

   private static class InnerHolder {
       private static final StaticInnerClassLazySingle SINGLE = new StaticInnerClassLazySingle("静态内部类");
   }

   public static StaticInnerClassLazySingle getInstance() {
       return InnerHolder.SINGLE;
   }

   public static void main(String[] args)throws Exception {
       //多线程跑
       for (int i = 0; i < 10; i++) {
           new Thread(){
               @Override
               public void run() {
                   StaticInnerClassLazySingle instance = StaticInnerClassLazySingle.getInstance();
                   System.out.println(instance.toString()+"---from:"+instance.getFrom());
               }
           }.start();
       }
       //反射创建
       Constructor<StaticInnerClassLazySingle> declaredConstructor = StaticInnerClassLazySingle.class.getDeclaredConstructor(String.class);
       declaredConstructor.setAccessible(Boolean.TRUE);
       StaticInnerClassLazySingle instance2 = declaredConstructor.newInstance("反射");
       //打印hasCode
       System.out.println(instance2.toString()+"---from:"+instance2.getFrom());
   }
}
静态内部类懒汉式特点:
  • 1.线程安全,jvm在加载该内部类的时候创建对象,不存在多线程问题
  • 2.可以通过反射破解
  • 3.必需要调getInstance()方法时才会加载内部类,对会new一个实例对象,实现了延迟加载
  • 4.没同步锁,不存在多线程下的同步开销问题

6.枚举式饿汉单例

Java

public enum EnumHungrySingle {
    INSTANCE("枚举创建");
    private String from;

    private EnumHungrySingle(String from) {
        this.from = from;
    }

    public String getFrom() {
        return from;
    }

    public static EnumHungrySingle getInstance() {
        return INSTANCE;
    }

    public static void main(String[] args) throws Exception {
        //多线程跑
        for (int i = 0; i < 10; i++) {
            new Thread() {
                @Override
                public void run() {
                    EnumHungrySingle instance = EnumHungrySingle.getInstance();
                    System.out.println(instance.toString() + "---from:" + instance.getFrom());
                }
            }.start();
        }
        //反射创建测试,结果会报“java.lang.IllegalArgumentException: Cannot reflectively create enum objects”
        Constructor<EnumHungrySingle> declaredConstructor = EnumHungrySingle.class.getDeclaredConstructor(String.class, int.class, String.class);
        declaredConstructor.setAccessible(Boolean.TRUE);
        EnumHungrySingle instance2 = declaredConstructor.newInstance("反射");
        System.out.println(instance2.toString() + "---from:" + instance2.getFrom());
    }
}
枚举式饿汉式特点:
  • 1.线程安全,枚举对象,在类加载时创建,不存在线程不安全问题
  • 2.无法通过反射破解
  • 3.类加载就创建,而不是调用getInstance()方法时对创建,不是延迟加载
  • 4.获取和创建过程都没同步锁,不存在多线程下的同步开销问题

单例的使用案例

1.简单饿汉式

Java Runtime

Runtime.getRuntime()

2.简单懒汉式

Java开源任务调度框架Quartz中的存储单例

SchedulerRepository repository = SchedulerRepository.getInstance();

spring中的单例

我们通常在说spring中的单例的时,大多时候讨论的是在spring创建的bean在默认的情况下是单例的。这与我们说的代码设计模式中的单例是两种概念。当我们所有的对象的创建动作都交由spring托管时,这些对象是否单例才能按照spring的规则来,如果我们自己去new一个controller或者service,则不能按照spring规则来讨论。
在默认的情况下,spring中的bean都是单例的。这种单例的实现并不是前面说的几种单例的实现方式,而是通过使用注册的形式将所有的创建好的bean注册中Map中,当使用的时候直接从中获取,不再创建。

总结

单例的实现方式有多种,根据项目需要,选择适合的即可,不需要过度实现。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值