设计模式-单例模式
单例模式模式一般大家都会认为它比较简单,其实并非你们所认为的那样,很多情况下,单例模式会涉及到很多优化,下面给大家简单介绍一下单例模式的几种演变过程:
- 饿汉模式
- 懒汉模式
- 懒汉模式(加锁关键字 synchronized)
- 懒汉模式(细粒度的添加synchronized)
- 懒汉模式(双重检查)
- 静态内部类
- 枚举类
第一种:饿汉模式 比较简单,类加载到内存的时候就进行实例化,推荐使用 ,但是有人会说,既然不用干嘛要进行实例化;
package com.dongl.singleton; /** * 饿汉模式 * 类加载到内存就直接实例化一个单例,JVM会保证它的线程安全 * 简单使用 推荐使用 * 缺点:无论用到与否 类加载就会直接实例化 * 有人就会吹毛求疵说:你不用 你实例化干嘛? */ public class T01_Singleton { private static T01_Singleton INSTANCE = new T01_Singleton(); public T01_Singleton() { } public static T01_Singleton getInstance(){ return INSTANCE; } public static void main(String[] args) { T01_Singleton t1 = T01_Singleton.getInstance(); T01_Singleton t2 = T01_Singleton.getInstance(); System.out.println(t1 == t2); } }
第二种:懒汉模式 这种模式在多线程的情况下会出现问题
package com.dongl.singleton; /** * 懒汉模式 lazy loading * 虽然达到了按需初始化的目的 但是也带来了线程安全的问题 * 在多线程的情况下 */ public class T02_Singleton { private static T02_Singleton INSTANCE = null; public T02_Singleton() { } public static T02_Singleton getInstance(){ if(INSTANCE == null){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new T02_Singleton(); } return INSTANCE; } public static void main(String[] args) { // T02_Singleton t1 = T02_Singleton.getInstance(); // T02_Singleton t2 = T02_Singleton.getInstance(); // System.out.println(t1 == t2); /**创建100个线程 调用getInstance() 打印返回的对象的hashcode()*/ for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(T02_Singleton.getInstance().hashCode()); }).start(); } } }
//运行结果 hashcode值不相同
E:\JDK\jdk1.8\bin\java.exe "-javaagent:E:\Idea\IntelliJ IDEA
1365279759
1365279759
1365279759
1945255694
226994615
第三种:懒汉模式(加锁关键字 synchronized)这种方式解决了懒汉模式下多线程问题,但是同时带来的问题是效率降低;
package com.dongl.singleton; /** * 懒汉模式 lazy loading * 虽然达到了按需初始化的目的 但是也带来了线程安全的问题 * 解决办法使用synchronized 但是带来的问题就是效率下降 */ public class T03_Singleton { private static T03_Singleton INSTANCE = null; public T03_Singleton() { } /** * 加锁的方式有两种 一种是对方法进行加锁 另一种是对代码块进行加锁 * 涉及到的无非是锁的粒度问题 * @return */ public static /**synchronized*/ T03_Singleton getInstance(){ synchronized (T02_Singleton.class) { if (INSTANCE == null) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new T03_Singleton(); } } return INSTANCE; } public static void main(String[] args) { /**创建100个线程 调用getInstance() 打印返回的对象的hashcode()*/ for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(T03_Singleton.getInstance().hashCode()); }).start(); } } }
第四种:懒汉模式(细粒度的添加synchronized)试图通过减小锁的粒度来进行改善效率的问题 但是不可行;
package com.dongl.singleton; /** * 懒汉模式 lazy loading * 虽然达到了按需初始化的目的 但是也带来了线程安全的问题 * 解决办法使用synchronized 但是带来的问题就是效率下降 * * 试图通过减小锁的粒度来进行改善效率的问题 但是不可行 */ public class T04_Singleton { private static T04_Singleton INSTANCE = null; public T04_Singleton() { } public static /**synchronized*/ T04_Singleton getInstance(){ if (INSTANCE == null) { //试图通过减小锁的粒度来进行改善效率的问题 但是不可行 synchronized (T02_Singleton.class) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new T04_Singleton(); } } return INSTANCE; } public static void main(String[] args) { /**创建100个线程 调用getInstance() 打印返回的对象的hashcode()*/ for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(T04_Singleton.getInstance().hashCode()); }).start(); } } }
第五种:懒汉模式(双重检查)这种方式解决了多线程带来的问题;
package com.dongl.singleton; /** * 懒汉模式 lazy loading * 虽然达到了按需初始化的目的 但是也带来了线程安全的问题 * 解决办法使用synchronized 但是带来的问题就是效率下降 * 因为锁的粒度很小也会带来多线程问题 * 这时可以使用双重检查 来避免 */ public class T05_Singleton { private volatile static T05_Singleton INSTANCE = null; public T05_Singleton() { } public static /**synchronized*/ T05_Singleton getInstance(){ if (INSTANCE == null) { synchronized (T02_Singleton.class) { //双重检查 if(INSTANCE == null) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new T05_Singleton(); } } } return INSTANCE; } public static void main(String[] args) { /**创建100个线程 调用getInstance() 打印返回的对象的hashcode()*/ for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(T05_Singleton.getInstance().hashCode()); }).start(); } } }
第六种:静态内部类实现懒加载(lazy loading)这种方式是最优解之一 因为在加载类的时候 不加载内部类这样就实现了懒加载;
package com.dongl.singleton; /** * 静态内部类的法方法 * JVM保证单例 * 加载外部类的时候 不会加载内部类 这样实现了懒加载 */ public class T06_Singleton { public T06_Singleton() { } //静态内部类 private static class T06_SingletonHandler{ private final static T06_Singleton INSTANCE = new T06_Singleton(); } public static T06_Singleton getInstance(){ T06_Singleton instance = T06_SingletonHandler.INSTANCE; return instance; } public static void main(String[] args) { /**创建100个线程 调用getInstance() 打印返回的对象的hashcode()*/ for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(T06_Singleton.getInstance().hashCode()); }).start(); } } }
第七种:枚举类 不仅可以解决线程同步,还可以防止反序列化。
package com.dongl.singleton; /** * 不仅可以解决线程同步,还可以防止反序列化。 */ public enum T07_Singleton { INSTANCE; public static void main(String[] args) { /**创建100个线程 调用getInstance() 打印返回的对象的hashcode()*/ for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(T07_Singleton.INSTANCE.hashCode()); }).start(); } } }
以上其中方式是按照问题出现 一步步的优化得到的,如果你觉得有疑问可以评论区说出你的观点,一起讨论一起进步!!!