单例模式(singleton)
singleton是一种创建型模式,指某个类采用singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并提供一个全局的访问点。
核心知识点:
1.将采用单例设计模式的类的构造方法私有化(采用private修饰)
2.在其内部产生该类的实例话对象,并将其封装成private static类型
3.定义一个静态方法返回该类的实例
单例模式总体上来说,分为两类:饿汉式和饱汉式(又叫懒汉式)
两种模式的区别在于:是自己被加载时就将自己实例化(饿汉式)还是第一次被引用时才将自己实例化饱汉式(又叫懒汉式)
下面是针对单例模式写的几个实现方式的demo
方式一:
使用饿汉模式,代码如下:
/**
* 单例模式实现方式一
* 饿汉式,线程安全,效率低
* @author liangxin
*
*/
public class Singleton1 {
//定义一个私有构造方法
private Singleton1() {}
//将自身低一个实例对象设置为一个属性,并加上static和final修饰符
private static final Singleton1 instance = new Singleton1();
//静态方法返回该类的实例
public static Singleton1 getInstance() {
return instance;
}
}
优点:写起来比较简单,而且不存在多线程同步问题,避免了synchronized所造成的性能问题;
缺点:当类singleton被加载的时候,会初始化static的instance,静态变量被创建并分配内存空间,从这以后,这个static的instance对象遍一直占着这段内存(即便你还没有用这个实例),当类被卸载时,静态变量被摧毁,并释放所占有的内存,因此在某些特定情况下会耗费内存。
方式二:
使用饱汉模式,代码如下:
/**
* 方式二
* 饱汉式,非线程安全
* @author liangxin
*
*/
public class Singleton2 {
//定义私有构造方法(防止通过new Singleton2()去实例化
private Singleton2() {}
//定义一个Singleton类型的变量(不初始化,注意这里没有使用final关键字
private static Singleton2 instance;
//定义一个静态的方法(调用是在初始化Singleton,但是多线程访问时,可能造成重复初始化问题)
public static Singleton2 getInstance() {
if(instance == null)
instance = new Singleton2();
return instance;
}
}
优点:写起来比较简单,当类singleton呗加载的时候,静态变量static的instance违背创建并分配内存空间,当getInstance方法第一次被调用时,初始化instance变量,并分配内存,因此在某些特定情况下会节约内存
缺点:并发环境下很可能出现多个singleton实例,
方式三
是方法二的简单优化,增加对多线程对支持
/**
* 方式三
* 饱汉式的优化升级版,饱汉式,线程安全简单实现
* @author liangxin
*
*/
public class Singleton3 {
//定义私有构造方法(防止通过new Singleton2()去实例化
private Singleton3() {}
//定义一个Singleton类型的变量(不初始化,注意这里没有使用final关键字)
private static Singleton3 instance;
//定义一个静态方法(调用时在初始化singleton,使用synchronized避免多线程访问时,可能造成重复初始化问题
public static synchronized Singleton3 getInstance() {
if(instance == null)
instance = new Singleton3();
return instance;
}
}
优点:使用synchronized关键字避免多线程访问时,出现多个singleton实例
缺点:同步方法频繁调用时,效率略低
方式四:
双重锁定,单例模式最佳实现,使用该方式,可使内存占用低,效率高,线程安全,多线程操作原子性
/**
* 单例模式最佳实现
* 内存占有低,效率高,线程安全,多线程操作原子性
* @author liangxin
*
*/
public class Singleton4 {
//定义一个私有构造方法
private Singleton4() {}
//定义一个静态私有变量(不初始化,不使用final关键字,使用volatile保证了多线程访问时instance变量的可见性,
//避免了instance初始化时其他比哪里属性还没赋值完时,被另外线程调用
private static volatile Singleton4 instance;
//定义一个共有的静态方法,返回该类型实例
public static Singleton4 getInstance() {
//对象实例化与否判断(不使用同步代码块,instance不等于null时,直接返回对象,提高运行效率)
if(instance == null) {
//同步代码块(对象未初始化时,使用同步代码块,保证多线程访问时对象在第一次创建后,不在重复被创建)
synchronized(Singleton4.class) {
//未初始化,则初始化instance变量
if(instance == null) {
instance = new Singleton4();
}
}
}
return instance;
}
}
方法四的实现使用了双重锁定,我们在使用该对象时,不用让线程每次都加锁,而只是在实例未被创建的时候在加锁处理,同时也能保证多线程的安全,这种做法被称为Double-CheckLocking(双重锁定)
对于方法四中为何要使用两个为空判断:
对于instance存在的情况,就不会进入该段逻辑,直接返回,OK,这是没问题的,但是当instance为null,并且同时有两个线程调用getInstance()方法时,他们将都可以通过第一重的instance == null判断,然后由于synchronized机制,这两个线程只有一个进入,另一个在外排队等待,必须要其中一个进入并出来之后,另一个才能进去,而如果没有第二重的instance == null判断,则第一个线程次创建了实例,而第二个线程还是可以继续创建新的实例,这样就没有达到单例的目的