又回到了这里,单例模式。
毕业前一直如雷贯耳,也曾亲密接触,但是只得其表不得其里。
终于毕业后在工作中用到了单例。那种感觉就是:“复行数十步,豁然开朗。土地平旷,屋舍俨然,有良田美池桑竹之属。”
正文:
先说一下学习链接,菜鸟教程。
什么是单例?
就是有这么一个类,它只能创建自己的对象,有且只有一个自己的对象。并向外部提供一个访问这个对象的方式。
单例解决了什么麻烦?(为什么使用单例)
1、如果一个类频繁的创建然后销毁,麻烦不麻烦?很麻烦,用单例,只创建一次,每次需要的时候直接调用就行。
2、控制实例数目,节省系统资源。
单例的特点是什么?(使用单例需要注意的地方)
1、构造函数必须私有化。
2、不能继承。
3、需要同步锁synchronized。
解释:
1、为什么构造函数必须私有化?如果不私有化,那默认是public,那就可以无限使用Singleton singleton = new Singleton();那就会不断实例化单例类。违反了单例的设计模式,提供不了单例所带来的优势。
2、为什么不能继承?因为构造函数私有化了。如果继承,子类的构造函数默认super();父类私有化的构造函数怎么super();?
3、为什么需要同步锁synchronized?防止多线程进入造成单例类被多次实例化。
单例代码怎么实现?
(线程安全是指:避免多个线程同时使用一个对象;不安全反之。)
(Lazy初始化是指:使用到的时候才进行初始化;反之就是在类加载的时候就初始化:即时加载)
1、懒汉式,线程不安全,Lazy初始化
不支持多线程,因为没有加锁,synchronized。
package test;
public class Singleton {
private Singleton(){}
private static Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2、懒汉式,线程安全,Lazy初始化
支持多线程,加锁synchronized,但是效率低。
package test;
public class Singleton {
private Singleton(){}
private static Singleton instance;
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3、饿汉式,线程安全,即时加载
没有加锁,执行效率高,类加载就初始化,浪费内存,容易产生垃圾。
即时加载容易理解,类初始化的时候,就要new Singleton();那既然没有synchronized,为什么还会线程安全?
因为classloader机制避免了多线程的同步问题。那什么是classloader机制?为什么避免了多线程?查了一些类加载的资料,还不是很懂,目前的理解是:多个线程同时实例化对象的时候,如果不存在Singleton对象,则触发类的初始化,如果存在Singleton对象,则直接调用。(类加载的文章链接点击这里)
package test;
public class Singleton {
private Singleton(){}
private static Singleton instance = new Singleton();
public synchronized static Singleton getInstance() {
return instance;
}
}
4、双检锁/双重校验锁(DCL,即 double-checked locking)
线程安全,懒加载,并且高性能。
其实这种单例我是没懂,单写出来记录一下。疑问点有两点。一是volatile关键字,看了例子,大概是不具备原子性,具备可见性。比synchronized更轻量级的同步机制,单原理和使用场景还没搞明白。二是双城instance == null 的目的。
package test;
public class Singleton {
private Singleton(){}
private volatile static Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
5、登记式/静态内部类//(此单例模式代码摘抄自菜鸟教程,因为这个好多地方我还没看懂)
这种单例模式是在饿汉式单例的升级版,为什么?因为在饿汉式的基础上达到lazy loading的效果。还能达到双检锁单例的功效。
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
6、枚举式
线程安全,非懒加载。(介绍枚举式单例详细的一篇博客)
实现单例最佳的方式,自动支持序列化机制,防止被多次实例化。
线程安全的原因:枚举类在被虚拟机加载的时候会保证线程安全的被初始化。
在序列化方面:枚举的序列化和反序列化是有特殊定制的。这就可以避免反序列化过程中由于反射而导致的单例被破坏问题。
线程安全自然不用多说,那么序列化和反序列化是什么呢?先把问题放在这儿。
package test;
public enum Singleton {
instance;//public static final Singleton instance;
}
网友评论