上次讲到工厂模式,可以说单例模式是除工厂模式之外,我们还算熟悉的另一种设计模式了
(对于我这种渣渣而言)。
单例模式其实很简单,就是某一个对象或者类,在程序运行期间,全局只初始化一次,其他
方法都使用同一个对象,这样对于某些特定的场景是有需求的,例如读取配置文件,没必要每
次读取都要重新new一个对象。
那么我们要实现一个单例,有很多方式,包括饿汉式、懒汉式、双重检测锁、静态内部类、
和枚举来实现,
1,下面是静态内部类实现:
//单例类
public class Singleton{
//构造函数
private Singleton(){}
//这里使用内部静态类来实现单例
private static Singleton createSingleton(){
private static Singleton singleton = new Singleton();
}
//对外提供获取单例的接口方法
public Singleton getSingleton(){
return createSingleton.singleton;
}
}
其实最推荐静态内部类实现,因为此方法运行期间不会主动加载,什么时候调用,什么时
候初始化实例,而且静态内部类由于其特性,天然的线程安全,而且内存占用第,效率高,那
么他就如此完美嘛?当然有缺点,就是静态类无法传参数。
2,下面是饿汉式实现单例
public class Singleton{
//构造
private Singleton(){}
//利用类加载器 静态初始化单例
private static Singleton singleton = new Singleton();
//給外部提供获取方法
public static getInstance(){
return singleton;
}
}
为什么叫饿汉,因为它在类加载的过程中就已经初始化了,已经是实例化过了,所以无论
你要不要,它都在那里,所以不能实现延迟加载。所以当你getInstance的时候没有延迟,随意
加载,调用效率极高。
3,下面是懒汉式实现单例
public class Singleton{
//构造
private Singleton(){}
//定义
private static Singleton singleton = null;
//給外部提供获取方法
public static synchronized getInstance(){
//如果单例为空 则初始化
if (singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
懒汉式顾名思义就是你什么时候用,我什么时候实例化,这样以来内存占用就比饿汉要
少一些,那么自然就可以实现延迟加载,但是调用效率并不高。对于懒汉式的一定要在
getInstance方法前加上Synchronized字段加锁,保证线程安全,不然就不再是单例了。
4,利用枚举来实现单例,这个我很少使用
public enum Singleton{
INSTANCE;
}
只需要记住枚举是天然的线程安全,在任何情况下它都是一个单例,所以我们一般配置
一些返回状态码时候,都会使用枚举类型。
5,双重锁实现单例,这个面试一般问的多些,但是平常并不使用,因为他偶尔会出问题。
public class Singleton {
private volatile static Singleton singleton = null;
private Singleton() {
}
public static Singleton getSingleton() {
//先判断对象是否已经实例化
if (singleton == null) {
//对整个类加锁
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
此方法先通过判断对象是否已经实例化,如果没有则加锁,再进行判断是否实例化。
因为有可能两个线程同时已经判断singleton为null时候,如果不加锁,有可能会被实例化两
次。所以先对整个类加锁,保证只有一个线程进行使用,再进行判断singleton,最后实例化。
在这里又一个修饰符是volatile, 那么它主禁用jvm的乱序重排功能。因为在singleton实
例化的过程中并非一个原子操作,而是由三部分组成:
1,首先为singleton分配内存空间
2,之后要进行对于singleton的初始化 构造方法
3,之后在把singleton指向分配的内存空间地址
由于jvm具有乱序执行,很有可能分配玩内存空间之后,直接指向内存地址了,在单线程
环境下不会出现问题。但是到多线程环境下,thread1刚刚1-3还没有执行2,thread2就来了,
此时调用getSingleton,发现singleton不为null,但是singleton并未初始化。
效率排序:
饿汉>=静态内部类>=枚举>双重锁>>懒汉
还需要多多学习,java真是博大精深,自己还是渣渣一枚!