单例模式(Singleton pattern)
Java单例设计模式中,要求JVM中只有这个类的一个实例,在此简单总结一下Java的几个实现该模式的方法。
- 方法一:饿汉式单例,有如下几个要点
- 构造方法私有化,不允许其他类创建本类的实例
- 由本类自己创建本类的一个实例
- 创建一个方法,通过方法来获取本类的实例
观察例子:
/*
* @author F3ver1
* @date 2018/11/26 22:29
*/
class Singleton {
//1.构造方法私有化
private Singleton(){
}
//2.在本类中创建实例
private static final Singleton SINGLETON = new Singleton();
//3.创建方法获得本类实例
public static Singleton getSingleton(){
return SINGLETON;
}
}
public class Singleton1{
public static void main(String[] args) {
Singleton singleton = Singleton.getSingleton();
Singleton singleton1 = Singleton.getSingleton();
System.out.println(singleton==singleton1);
}
}
得到结果为true,即该类只能得到一个实例
- 方法二:懒汉式单例,要点如下(仅仅适用于单线程环境下,稍后再说)
- 构造方法私有化
- 在本类中声明本类实例
- 利用方法来创建对象
代码如下:
/*
* @author F3ver1
* @date 2018/11/26 22:39
*/
class Singleton{
//1.构造方法私有化
private Singleton(){
}
//2.声明本类对象
private static Singleton SINGLETON;
//3.通过方法创建本类对象
public static Singleton getSINGLETON(){
if(SINGLETON == null) {
SINGLETON = new Singleton();
}
return SINGLETON;
}
}
public class Singleton2 {
public static void main(String[] args) {
Singleton singleton = Singleton.getSINGLETON();
Singleton singleton1 = Singleton.getSINGLETON();
System.out.println(singleton==singleton1);
}
}
以上代码在单线程环境下毫无问题,但是如果在多线程下就不再能完成任务,也就是多线程以上代码是可能出现多个实例的,当线程1判断if(SINGLETON==null)时,线程2同时也判断该语句,则两个线程都认为此时SINGLETON的值都为NULL,而未实例化,因此就不再是单例了,在这里只需要在静态方法getSINGLETON加上关键字Synchronized即可。
- 方法三:懒汉式优化
在方法二中,我们认为在方法上加上Synchronized关键字有损效率,因为在多线程下,这个方法只需要锁住第一次即可,也就是说,只要保证第一次创建对象时,其他线程无法干扰就可以了,但是在方法上加上Synchronized关键字,每个线程执行该方法都会上一次锁,这样效率影响不小,因此这里给出更好的版本
/*
* @author F3ver1
* @date 2018/11/28 19:23
*/
class Singleton{
//1.构造方法私有化
private Singleton(){
}
static {
System.out.println("Singleton类被加载");
}
//2.创建私有静态内部类,通过类来创建对象且保证单例性
private static class Single{
static {
System.out.println("Single类被加载");
}
static Singleton SINGLETON = new Singleton();
}//3.通过本类方法加载内部类
public static Singleton getSINGLETON(){
return Single.SINGLETON;
}
public static void test(){
System.out.println("Singleton类其他方法被加载");
}
}
public class Singleton3 {
public static void main(String[] args) {
Singleton.test();
}
}
结果打印:
Singleton类被加载
Singleton类其他方法被加载
结果得知当调用Singleton其他方法时并不会加载其内部类而创建对象,仅仅只有当调用Singleton中的getSINGLETON方法才能得到其对象
public class Singleton3 {
public static void main(String[] args) {
Singleton singleton = Singleton.getSINGLETON();
Singleton singleton1 = Singleton.getSINGLETON();
System.out.println(singleton==singleton1);
}
}
结果得到true
这里我们依靠getSINGLETON()方法来使Singleton类中的内部类得到加载,因为类的加载一次运行只能存在一次,之后再调用类不会再被重复加载,依靠这个特点,来创建单例对象,并且一定是线程安全的。
- 方法四:枚举实现
枚举用于对象数目固定的场景,因此也可以用于单例模式,给出最简单的情况参考
/*
* @author F3ver1
* @date 2018/11/28 19:59
*/
enum Singleton{
SINGLETON;
Singleton(){
}
}
public class Singleton4 {
public static void main(String[] args) {
Singleton singleton = Singleton.SINGLETON;
Singleton singleton1 = Singleton.SINGLETON;
System.out.println(singleton==singleton1);
}
}
结果同样得到true。