前言
以前对设计模式没有太大感觉。现在慢慢在工作中越来越感觉设计模式的重要性,所以决定重头来好好学习整理下常用的设计模式。
本文介绍的最初级的一个设计模式 单例模式
模式说明
java中单例模式定义:一个类有且仅有一个实例,并且自行实例化向整个系统提供。
应用: 一个系统中多线程对一个文件进行操作,这个文件就需要保证只有一个实例,等等
饿汉式
说明:饿汉式意思就是无论系统需不需要使用该类的实例对象,先在类加载的时候创建好
优点:实现简单
缺点:容易造成内存的浪费
常用饿汉式
该方式是在该类被加载的时候,就创建好了实例化对象。不允许其他地方通过构造方法来创建对象,只能通过提供的指定公共方法来获取唯一的对象。
public class SingletonDemo1 {
//static 确保在类加载的时候就会初始化该对象
private final static SingletonDemo1 singleton = new SingletonDemo1();
// 必须是私有构造方法,防止其他地方通过构造方法来实例化对象
private SingletonDemo1(){
}
// 是一个公共的静态方法,用于对外提供唯一的对象
public static SingletonDemo1 getInstance(){
return singleton;
}
}
枚举
根据枚举的特性,可以使用枚举来实现单例模式
public class SingletonDemo3 {
// 不允许其他地方通过构造方法进行实例化对象
private SingletonDemo3(){}
//对外提供可获取唯一实例化对象的公共方法
public static SingletonDemo3 getInstance(){
return Singleton.SINGLETON.getInstance();
}
private enum Singleton{
SINGLETON;
private SingletonDemo3 singleton;
Singleton(){
singleton = new SingletonDemo3();
}
public SingletonDemo3 getInstance(){
return singleton;
}
}
}
懒汉式
说明:懒汉式意思就是在系统第一次需要使用该类的实例对象时,再创建该类的实例对象。
优点:不造成内存没必要的浪费
缺点:实现相对较难
懒汉式-线程不安全
在有多线程使用的情况下不要使用,否则可能会造成创建多个实例对象。一般建议不使用该种方式。
public class SingletonDemo1 {
//static 确保在类加载的时候就会初始化该对象
private static SingletonDemo1 singleton = null;
// 必须是私有构造方法,防止其他地方通过构造方法来实例化对象
private SingletonDemo1(){
}
// 是一个公共的静态方法,用于对外提供唯一的对象
public static SingletonDemo1 getInstance(){
if(singleton==null){
singleton = new SingletonDemo1();
}
return singleton;
}
}
懒汉式-线程安全-同步方法
由于上面的方式,是在执行getInstance()方法时,多线程使用时可能出现创建多个对象的情况。那可以直接将该方法进行同步,保证任意时间,最多是能一个线程使用该方法。
public class SingletonDemo2 {
//static 确保在类加载的时候就会初始化该对象
private static SingletonDemo2 singleton = null;
// 必须是私有构造方法,防止其他地方通过构造方法来实例化对象
private SingletonDemo2(){
}
// 是一个公共的静态方法,用于对外提供唯一的对象
public static synchronized SingletonDemo2 getInstance(){
if(singleton==null){
singleton = new SingletonDemo2();
}
return singleton;
}
}
缺点:太重了,每个线程在每次使用的时候,都需要进行同步,而实际上只是在第一次获取该类的实例对象时需要同步,往后使用不需要同步。
懒汉式-线程安全-双重锁
该方式只会在未实例化对象时,才同步进行实例化对象。实例化后就不需要再进行同步了。
public class SingletonDemo2 {
//static 确保在类加载的时候就会初始化该对象
private static volatile SingletonDemo2 singleton = null;
// 必须是私有构造方法,防止其他地方通过构造方法来实例化对象
private SingletonDemo2(){
}
// 是一个公共的静态方法,用于对外提供唯一的对象
public static SingletonDemo2 getInstance(){
if(singleton==null){
synchronized (SingletonDemo2.class){
if(singleton==null) { //为了防止多个线程进行实例化
singleton = new SingletonDemo2();
}
}
}
return singleton;
}
}
有么有注意到,使用了volatitle关键字,为什么要使用volatile关键字?
答:这里使用了volatile一个重要的特性——禁止JVM指令重排序
singleton = new SingletonDemo2()的在jvm内存中会有三步,1)分配内存;2)初始化对象;3)将对象指向分配的内存。jvm在会在不改变最终执行结果的情况下对指令进行重排序,即:可能执行顺序是1->3->2,若是在执行执行3还未执行2时,其他线程执行使用,则会报错。
(参考:https://www.cnblogs.com/crossoverJie/p/9326788.html)
参考资料
https://www.cnblogs.com/paul011/p/8574650.html
http://www.runoob.com/design-pattern/singleton-pattern.html
java实例化对象过程说明:https://blog.csdn.net/justloveyou_/article/details/72466416