【简介】
单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例)
【场景】
现实中有意思的场景可以查阅小编之前总结的一篇博客:找对象需要单例模式吗?
适用单例的条件:
一、某个类只能有一个实例
二、是它必须自行创建这个实例
三、是它必须自行向整个系统提供这个实例。
单例具体实现要求:
一、是单例模式的类只提供私有的构造函数
二、是类定义中含有一个该类的静态私有对象
三、是该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。
【示例】
一、饿汉式,私有静态变量初始化方式(简单易用,能满足大部分要求)
说明:类加载到内存后,就实例化一个单例,jvm保证线程安全(缺点:不管用到与否,类装载时就会完成实例化)
public class Mar1 { private static Mar1 INSTANCE = new Mar1(); private Mar1(){} public static Mar1 getInstance(){ return INSTANCE; } public static void main(String[] args) { Mar1 mar1 = Mar1.getInstance(); Mar1 mar11 = Mar1.getInstance(); System.out.println(mar1 == mar11); } }
二、懒汉式,锁方法
说明:需要用到的时候再进行实例创建(缺点:并发带来效率低下的问题)
注:volatile关键字可以规避指令重排带来的不安全性(idea中可以查看编辑指令)
public class Mar4 { private static volatile Mar4 INSTANCE; private Mar4(){} public static synchronized Mar4 getInstance(){ if(INSTANCE == null){ //测试并发加入的睡眠 try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new Mar4(); } return INSTANCE; } public static void main(String[] args) { for (int i = 0; i<100;i++){ new Thread(()->{ System.out.println(Mar4.getInstance().hashCode()); }).start(); } } }
三、懒汉式,局部锁,双重判定
说明:需要用到的时候再进行实例创建,相对于二的效率得到了一些提升
public class Mar6 { private static volatile Mar6 INSTANCE; private Mar6(){} public static Mar6 getInstance(){ if(INSTANCE == null){ synchronized(Mar6.class){ if(INSTANCE == null) { //测试并发加入的睡眠 try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new Mar6(); } } } return INSTANCE; } public static void main(String[] args) { for (int i = 0; i<100;i++){ new Thread(()->{ System.out.println(Mar6.getInstance().hashCode()); }).start(); } } }
四、懒汉式,内部类实现
说明:类加载的时候不会去加载内部类,当使用到调用的时候才会进行实例化
public class Mar7 { private Mar7(){} private static class Mar7Holder{ private static Mar7 INSTANCE = new Mar7(); } public static Mar7 getInstance(){ return Mar7Holder.INSTANCE; } public static void main(String[] args) { for (int i = 0; i<100;i++){ new Thread(()->{ System.out.println(Mar7.getInstance().hashCode()); }).start(); } } }
五、枚举方式(effectice java书中提到的)
说明:不仅可以解决线程安全还可以防止反序列化
public enum Mar8 { INSTANCE; public static void main(String[] args) { for (int i = 0; i<100;i++){ new Thread(()->{ System.out.println(Mar8.INSTANCE.hashCode()); }).start(); } } }
【总结】
通过学习各种形式的单例模式,对单例又有了一个更加深刻的认识。
1.利用java中的特性来解决(内部类、枚举类)
2.使用锁的时候要在保证安全的前提控制锁的粒度
3.通过学习单例对以后code更有干劲
4.激发了自己了解其它模式更加浓厚的兴趣与爱好