单例设计模式
由于某些类创建对象可能会耗费内存和花费时间。一般将这种类设计为单例设计模式会比较好。
1.对象在内存中只有一个,减少了内存的开销
2.可以控制对象的创建时刻
单例模式的特点:
1.单例的类在整个JVM中只有一个实例
2.需要提供一个全局访问点(1.公开的静态变量,2.公开的静态方法)
1.饿汉式:
特点:类加载的时候就创建了实例
//饿汉式 /* *1.类能被创建且只有一个实例 * 2.提供一个全局的访问点 * */ public class Singleton { //创建唯一的实例对象 private static Singleton singletonInstance = new Singleton(); //修改默认的构造器为私有的,屏蔽外部的new方法 private Singleton() {} //全局的访问点 public static Singleton getInstance() { return singletonInstance; } }
2.懒汉式
特点:在需要创建实例的时候才调用方法创建实例对象
class Singleton2 { //保存唯一的实例对象 private static Singleton2 singletonInstance; //修改默认的构造器为私有的,屏蔽外部的new方法 private Singleton2() {} //全局的访问点 public static Singleton2 getInstance() { if(singletonInstance == null) { singletonInstance = new Singleton2(); } return singletonInstance; } }
3.加锁的懒汉式
由于一般懒汉式不能保证线程安全。所以需要在方法加锁保证线程安全
//加锁的懒汉式 class Singleton3 { //保存唯一的实例对象 private static Singleton3 singletonInstance; //修改默认的构造器为私有的,屏蔽外部的new方法 private Singleton3() {} //全局的访问点 //给创建实例的方法加锁。防止在多线程条件下线程问题 public synchronized static Singleton3 getInstance() { if(singletonInstance == null) { singletonInstance = new Singleton3(); } return singletonInstance; } }
4.双重验证的懒汉式
由于枷锁的懒汉式对整个方法加了锁,会导致每次调用创建实例方法都会需要进行等待,但是如果实例已经创建了,应该是不想继续等待的。所以应该只在判断实例是否创建的地方加锁即可
class Singleton4 { //保存唯一的实例对象 private static Singleton4 singletonInstance; //修改默认的构造器为私有的,屏蔽外部的new方法 private Singleton4() {} //全局的访问点 public static Singleton4 getInstance() { if(singletonInstance == null) { //获取单例类的锁 synchronized (Singleton4.class) { if(singletonInstance == null) { singletonInstance = new Singleton4(); } } } return singletonInstance; } }
5.加上volatile关键字防止重排序的双重验证懒汉式
创建一个对象可以分为3步:
虽然重排序不会影响单线程的执行结果,但是由于判断的条件是instance == null,当分配了内存以后,其他线程来到判断的地方,instance不为空,所以直接将引用指向实例对象。但是实例对象还没有初始化,就会出现问题。
所以需要放防止重排序
class Singleton5 { //volatile防止重排序 private volatile static Singleton5 singletonInstance; //修改默认的构造器为私有的,屏蔽外部的new方法 private Singleton5() {} //全局的访问点 //给创建实例的方法加锁。防止在多线程条件下线程问题 public static Singleton5 getInstance() { if(singletonInstance == null) { //获取单例类的锁 synchronized (Singleton5.class) { if(singletonInstance == null) { singletonInstance = new Singleton5(); } } } return singletonInstance; } }
6.静态内部类的方式:通过类加载过程的线程安全来保证创建单例实例的线程安全
class Singleton6 { //修改默认的构造器为私有的,屏蔽外部的new方法 private Singleton6() {} //静态内部类保存唯一的实例对象 private static class InnerInstanceHolder{ private static Singleton6 singletonInstance = new Singleton6(); } //类加载是线程安全的,所以使用内部类持有唯一实例是线程安全的,且效率比较高 public static Singleton6 getInstance() { return InnerInstanceHolder.singletonInstance; } }
7.枚举方式
//枚举方式 public enum Singleton { INSTANCE; public void method() { } }
暂时还没想懂枚举方式的特点。列出参考网站下次研究:
为什么我墙裂建议大家使用枚举来实现单例
图片参考来源:https://blog.csdn.net/qq_36109365/article/details/78090096