Singleton模式
意图
保证一个类仅有一个实例,并提供一个访问他的全局访问点
How
我们怎么保证一个类只有一个实例并且这个实例易于被访问呢?一个全局变量使得一个对象可以被访问,但它不能防止你实例化多个对象。解决方案就是,我们可以让类本身负责他的唯一实例的访问保存
,通过拦截创建实例的请求,判断是否有已经存在的实例,没有就创建实例,有则直接返回实例,这就是单例模式。
适用性
1、当类只能有一个实例且客户可以从一个全局的访问点访问他
2、当这个唯一实例应该是通过子类化可扩展的,比拿给客户应该无需修改代码就能使用一个扩展的实例时
常见的实例
线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序等对象常常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler(打印假脱机),以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,单例模式就是为了避免不一致状态,避免政出多门。
结构
Singleton
定义一个Instance操作,允许客户访问他的唯一实例。Instance是一个类操作
可能负责创建他自己的唯一实例
优点
1、对唯一实例的受控访问
2、缩小名空间
3、允许对操作和表示的精化
4、允许可变数目的实例
5、比类操作更灵活
实现
1、饿汉式
package singleton;
/**
* @Author fitz.bai
* @Date 2018/8/28 10:58
*/
/**
* 1、饿汉式单例在类加载初始化时就创建好一个静态的对象供外部使用,
* 除非系统重启,否则这个对象不会改变,本身线程安全。
*
* 2、通过为private的构造方法避免了类在外部被实例化,在同一个虚
* 拟机范围内,单例模式唯一实例只能通过getInstance()方法访问。
*/
public class HungrySingleton {
private HungrySingleton() {}
private static HungrySingleton singleton = new HungrySingleton();
public static HungrySingleton getInstance() {
return singleton;
}
}
2、懒汉式
package singleton;
/**
* @Author fitz.bai
* @Date 2018/8/28 10:58
*/
/**
* 1、延迟加载方式,只有在调用getInstance()方法时才创建
*
* 2、多线程环境下会产生多个single对象,因此线程不安全
*/
public class LazySingleton {
private LazySingleton() {}
private static LazySingleton singleton = null;
public static LazySingleton getInstance() {
if (singleton == null) {
singleton = new LazySingleton();
}
return singleton;
}
}
VS 与问题
饿汉式单例模式:线程安全
,在类加载时就完成了初始化,所以类加载比较慢,但获取对象的速度快。没有lazy loading的效果,如何一直不使用,会造成内存浪费
懒汉式单例模式:懒加载
,在类加载时不初始化。但是线程不安全
。如果在多线程下,一个线程进入了if (singleton == null)判断语句块,此时,另一个线程也通过了这个判断语句,这时判断都为true,便会产生多个实例
。所以在多线程环境下不可使用
这种方式。
如何解决懒汉式单例模式的线程安全问题呢?
1、同步方法(不推荐)
package singleton;
/**
* @Author fitz.bai
* @Date 2018/8/28 10:58
*/
/**
* 1、效率太低,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。不推荐使用
*/
public class SyncLazySingleton {
private SyncLazySingleton() {}
private static SyncLazySingleton singleton;
public static synchronized SyncLazySingleton getInstance() {
if (singleton == null) {
singleton = new SyncLazySingleton();
}
return singleton;
}
}
2、同步代码块(warning:不能解决
)
package singleton;
/**
* @Author fitz.bai
* @Date 2018/8/28 10:58
*/
/**
* 1、不能解决线程安全问题,跟之前一样,假如一个线程进入了
* if (singleton == null)判断语句块,此时,另一个线程
* 也通过了这个判断语句,这时便会产生多个实例。
*/
public class SyncBlockLazySingleton {
private SyncBlockLazySingleton() {
}
private static SyncBlockLazySingleton singleton;
public static SyncBlockLazySingleton getInstance() {
if (singleton == null) {
synchronized (SyncBlockLazySingleton.class) {
singleton = new SyncBlockLazySingleton();
}
}
return singleton;
}
}
双检索(推荐)
package singleton;
/**
* @Author fitz.bai
* @Date 2018/8/28 10:58
*/
/**
* 1、volatile的重要性,在线程技术那边说过,可以参考
*/
public class DoubleCheckSingleton {
private DoubleCheckSingleton() {
}
private static volatile DoubleCheckSingleton singleton;
public static DoubleCheckSingleton getInstance() {
if (singleton == null) {
synchronized (DoubleCheckSingleton.class) {
if (singleton == null) {
singleton = new DoubleCheckSingleton();
}
}
}
return singleton;
}
}
静态内部类(推荐)
package singleton;
/**
* @Author fitz.bai
* @Date 2018/8/28 10:58
*/
/**
* 1、采用类加载的机制来保证初始化实例时只有一个线程(static final)
* 2、Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用
* getInstance()方法,才会装载完成Singleton的实例化(Lazy Loading)。
* 3、类的静态属性只会在第一次加载类的时候初始化,JVM帮助我们保证了线程
* 的安全性,在类进行初始化时,别的线程是无法进入的。
*/
public class InnerStaticSingleton {
private InnerStaticSingleton() {
}
private static class SingletonInstance {
private static final InnerStaticSingleton INSTANCE = new InnerStaticSingleton();
}
public static InnerStaticSingleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
枚举(很强,但是好像使用的人较少)
package singleton;
/**
* @Author fitz.bai
* @Date 2018/8/28 10:58
*/
/**
*
* 自由序列化,线程安全,保证单例
*
* 1、enum是由class实现的,由于enum是通过继承了Enum类实现的,enum结构
* 不能够作为子类继承其他类,但是可以用来实现接口。此外,enum类也不能够被继承,
* 在反编译中,我们会发现该类是final的。
*
* 2、enum有且仅有private的构造器,防止外部的额外构造,这恰好和单例模式吻合,
* 也为保证单例性做了一个铺垫。这里展开说下这个private构造器,如果我们不去手写
* 构造器,则会有一个默认的空参构造器,我们也可以通过给枚举变量参量来实现类的初
* 始化。
*
*/
public class EnumSingleton {
private EnumSingleton() {
}
public static EnumSingleton getInstance() {
return Singleton.INSTANCE.getInstance();
}
private static enum Singleton {
INSTANCE;
private EnumSingleton singleton;
// 省略了private,但是他并不是默认的权限,仍然是private
Singleton() {
singleton = new EnumSingleton();
}
public EnumSingleton getInstance() {
return singleton;
}
}
}
单例模式在Java中的应用
Runtime,JDK API对于这个类的解释”每个Java应用程序都有一个Runtime类实例,使应用程序能够与其运行的环境相连接,可以通过getRuntime方法获取当前运行时。应用程序不能创建自己的Runtime类实例。”就是完美的单例模式的应用适用。
以上就是个人单例模式的理解,希望大家有问题可以提出,共同学习。