两种模式
- 饿汉模式
饿汉模式:加载类时比较慢,运行时获取对象的速度快,线程安全
- 懒汉模式
懒汉模式:加载类时比较快,运行时获取对象的速度慢,线程不安全
实现思路
单例模式要求类能够返回对象一个引用(永远是同一个)和一个获取该实例的方法(必须是静态方法)
实现步骤
1.将构造方法私有化,不允许外界直接创建对象
2.声明唯一的实例,使用private static修饰
3.提供一个public static 修饰的获取实例的方法
注意事项
懒汉模式是线程不安全的,当唯一实例未创建时,同时有两个线程调用创建方法,又同时检测到没有唯一实例,而各自创建了一个实例,这时就有两个实例构造出来了,违反了单例模式唯一的原则。让我们接着往下看,有什么解决方法
静态模式的八种写法
1.饿汉模式(静态常量)【可用】
public class Singleton{
// 将构造方法私有化,不允许外界直接创建对象
private Singleton(){}
// 声明唯一实例
private final static Singleton instance = new Singleton();
// 提供一个获取实例的方法
public static Singleton getInstance(){
return instance;
}
}
优点:写法简单,在类加载的时候就完成实例化,避免线程同步问题
缺点:在类加载的时候完成实例的创建,没有达到Lazy Loading的效果。如果没有用过这个实例,会造成内存浪费
2.饿汉模式(静态代码块)【可用】
public class Singeton{
private static Singleton instance;
static{
instance = new Singleton();
}
private Singleton(){}
public static Singleton getInstance() {
return instance;
}
}
优点:同上,不过将类实例化的过程放在静态代码块中,在类加载的时候,执行静态代码块里面的代码,初始化类实例
缺点:同上
3.懒汉模式(线程不安全)【不可用】
public class Singeton{
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance() {
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
优点:起到Lazy Loading效果
缺点:只能在单线程时使用,可能同时两个线程进入判读语句,产生多个实例
4.懒汉模式(线程安全,同步方法)【不可用】
public class Singeton{
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance() {
if(singleton == null){
singleton = new Singleton();
}
return instance;
}
}
优点:解决了上面线程不安全的问题,对getInstance()方法进行了线程同步
缺点:效率太低,每个线程想获取类的实例时,执行getInstance()方法都要进行同步,其实只有第一次对象未实例化时需要同步,以后直接return就可以了
5.懒汉模式(线程安全,同步代码块)【不可用】
public class Singeton{
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance() {
if(singleton == null){
synchronized (Singleton.class){
singleton = new Singleton();
}
}
return instance;
}
}
改进了上一个方法,采用同步代码块,但是并不能起到线程同步的作用,如果同时两个线程进入判读,虽然实例化的部分加上了锁,同时只有一个线程可以实例化,但是待第一个线程释放了锁,第二个线程也会实例化一次(已经进入了判读),产生多个实例
6.懒汉模式(双重检查 )【推荐用】
public class Singeton{
private static volatile Singleton instance;
private Singleton(){}
public static Singleton getInstance() {
if(singleton == null){
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return instance;
}
}
Double-Check,保证线程安全,实例化的代码也只会执行一次。线程安全,Lazy Loading,效率高。
7.似饿汉模式(静态内部类)【推荐用】
public class Singeton{
private Singleton(){}
private static class SingletonInstance{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
和懒汉模式类似,但有所区别,两者都采用了类装载的机制,保证初始化实例的只有一个线程,不同的地方在于,饿汉模式只要Singleton类被装载就会实例化,没有Lazy Loading,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化的地方,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。类的静态属性只会在第一次加载类的时候初始化,所以JVM保证了线程的安全
优点:线程安全,Lazy-Loading,效率高
7.枚举【推荐用】
public enum Singeton{
INSTANCE;
public void anyMethod() {
}
}
借助JDK1.5添加的枚举类实现单例模式。不仅避免了多线程同步问题,还能反正反序列化重新创建新的对象。卵秀飞
优点:真的秀
单例模式的优点
系统内存改类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统的性能。
单例模式的缺点
当想实例化一个单例类的时候,必须记住使用相应的获取对象的方法,而不是直接new,可能给其它开发人员造成困扰
单例模式适用场景
- 需要频繁创建销毁的对象
- 创建很耗资源,当使用频繁
- 工具类对象
- 频繁访问数据库或文件的对象
为什么实例必须为静态,获取实例的方法也必须为静态?
你只要弄明白单例模式是如何实现的,就能从本质上理解这个问题;
单例模式实现过程如下:
首先,将该类的构造函数私有化(目的是禁止其他程序创建该类的对象);
其次,在本类中自定义一个对象(既然禁止其他程序创建该类的对象,就要自己创建一个供程序使用,否则类就没法用,更不是单例);
最后,提供一个可访问类自定义对象的类成员方法(对外提供该对象的访问方式)。
直白的讲就是,你不能用该类在其他地方创建对象,而是通过该类自身提供的方法访问类中的那个自定义对象。
那么问题的关键来了,程序调用类中方法只有两种方式,①创建类的一个对象,用该对象去调用类中方法;②使用类名直接调用类中方法,格式“类名.方法名()”;
上面说了,构造函数私有化后第一种情况就不能用,只能使用第二种方法。
而使用类名直接调用类中方法,类中方法必须是静态的,而静态方法不能访问非静态成员变量,因此类自定义的实例变量也必须是静态的。
这就是单例模式唯一实例必须设置为静态的原因。
参考地址:
网友评论