单例模式
概述
单例模式,它的定义就是确保某一个类只有一个实例,并提供一个全局的访问点。
特点
1. 不允许其他程序调用new对象
2. 在该类中创建对象
3. 对外提供一个可以让其他程序获取对象的方法
因此当系统中只需要一个实例对象或者系统中只允许一个公共访问点,除了这个公共访问点外,不能通过其他访问点访问该实例时,可以使用单例模式
优点
1. 节约系统资源、提高了系统效率
2. 能够严格的控制客户对它的访问
缺点
1. 会导致单例类的职责过重,违背了“单一职责原则”
2. 由于没有抽象类,所以拓展起来有一定的困难
实现
1. 私有化该类的构造函数
2. 通过new在本类中创建一个本类对象
3. 定义一个公有的方法,将在该类中所创建的对象返回
写法
单例模式的写法可以分为五种:①懒汉式、②饿汉式、③双重校验锁、④静态内部类、⑤枚举
饿汉式
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
优点
这种方式实现比较简单,在类加载的时候就完成了实例化,避免了线程同步的问题
缺点
由于在类加载的时候就实例化了,没有达到懒加载(Lazy Loading)的效果,也就是说我不用它也会加载,会造成内存的浪费(这个浪费可以忽略不计,所以这种方式也是推荐使用的)
如果初始化本身依赖于一些其他的数据,那么很难确保其他数据在它初始化之前准备好
变换写法
public class Singleton{
private static Singleton instance = null;
static{
instance = new Singleton();
}
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
懒汉式
写法一[线程不安全,不可用]
public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null) instance = new Singleton();
return instance;
}
}
这种写法会造成线程安全问题,可能会同时创建两个对象
写法二[线程安全,效率低,不推荐]
public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance == null) instance = new Singleton();
return instance;
}
}
执行效率太低了,每个线程想要获得类的实例的时候,执行getInstance()方法都要进行同步,这个方法只要执行一次就行了,后面想要获取直接return
改进[线程不安全,不可用]
public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null) {
synchronized(Singleton.class){
instance = new Singleton();
}
}
return instance;
}
}
可能会有第二个线程进入到if语句块,当第一个线程执行完毕以后,第二个线程还会再创建一个对象,于是就有大神写出了懒汉式双重校验锁
双重校验锁【推荐】
public class Singleton{
/**
* 懒汉式变种,属于懒汉式中最好的写法,
* 保证了:延迟加载和线程安全
*/
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null) {
synchronized(Singleton.class){
if(instance == null) instance = new Singleton();
}
}
return instance;
}
}
我们进行两次if(instance == null检查,这样就可以确保线程安全了),后面再进行访问时就会直接return实例对象了
优点
线程安全、延迟加载、效率较高
缺点
上面代码看起来似乎已经是非常完美的了,但其实还是有一点小瑕疵,类还未装载完成就已经被其他线程在调用了
改进
对于上面方法进行了改进,在给instance的声明加上volatile关键字即可,volatile关键字的作用是禁止指令重排,这样在赋值完成之前是不允许进行读操作的
public class Singleton{
private static volatile Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null) {
synchronized(Singleton.class){
if(instance == null) instance = new Singleton();
}
}
return instance;
}
}
静态内部类【推荐】
public class singleton{
private Singleton(){}
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
public static final Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
在Singleton类被装载时并不会被实例化,而是在调用getInstance方法的时候,才会去装载SingletonHohder类,从而完成Singleton的实例化。
为什么能保证线程安全呢?
类的静态属性只会在第一次加载类的时候初始化,JVM帮助我们保证了线程的安全性,在类进行初始化的时候,别的线程是无法进入的
优点
线程安全,延迟加载,效率高
枚举【强力推荐】
public enum SingletonEnum{
INSTANCE;
private SingletonEnum(){}
public void method(){
}
}
枚举的书写非常简单,访问方式SingletonEnum.INSTANCE这里的INSTANCE即为SingletonEnum类型的引用,得到它就可以调用枚举中的方法
参考博客
https://www.cnblogs.com/pony1223/p/7608955.html
http://blog.csdn.net/dmk877/article/details/50311791
https://www.cnblogs.com/dongyu666/p/6971783.html