Hey Singleton
Never put off what you can do today until tomorrow. – 今日事今日毕.
最近在看一本书,叫做java性能优化指南,收获颇丰,推荐大家有时间可以看一看,毕竟性能优化一直是我们的一个劣势.
大家都知道,单例模式是一种非常重要的结构模式,因此如何写出高效的单例模式也是很重要的,现在的面试中,有的面试官也会经常问一些单例问题,所以基于此,下面给出了七种方法实现单例.
- 懒汉式
第一种 线程不安全
public class Singleton{
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
第二种 线程安全
public class Singleton{
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
第三种 双重校验锁 线程安全
双重判断 第一个判断是为了提高性能,第二个判断是第一次实例化需要.
/**
* 懒汉式 单例
*/
public class SingletonOne {
private SingletonOne() {
System.out.println("创建了Singleton");
}
// 私有属性
private static SingletonOne singletonOne = null;
public static SingletonOne getSingletonOne() {
// 提高性能
if (singletonOne == null) {
synchronized (SingletonOne.class) {
// 判断是否含有此对象
if (singletonOne == null) {
singletonOne = new SingletonOne();
}
}
}
// 返回此单例
return singletonOne;
}
}
第四种 静态内部类 (线程安全)
通过静态内部类的方式进行创建单例,基于类加载器的实现机制,完成了单例的创建,效率更高,并且不用加锁,当静态类加载的时候,并没有创建该单例,只有调用方法时,才会进行创建单例.
/**
* 懒汉式 单例模式
*/
public class SingletonTwo {
// 私有构造方法
private SingletonTwo(){
System.out.println("创建了单例");
}
// 静态内部类
private static class SingletonHolder{
private static SingletonTwo singletonTwo = new SingletonTwo();
}
// 得到 单例
public static SingletonTwo getInstance(){
return SingletonHolder.singletonTwo;
}
}
第五种 枚举 线程安全
public enum SingleTon {
INSTACNE; //理解为public static final Singleton INSTANCE;
SingleTon(){
}
int getValue(){
return 1+90;
}
}
测试:
public static void main(String[] args) {
SingleTon instacne = SingleTon.INSTACNE;
int value = instacne.getValue();
System.out.println(value);
}
这样很简单,线程时安全的,并且避免了序列化和反射攻击.
- 饿汉式
第六种 饿汉式 通过静态赋值 得到 单例对象 线程安全
这种方式基于classloader机制,避免了多线程的同步问题
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton () { }
public static Singleton getInstance() {
return instance;
}
}
第七种 通过静态代码块 对单例对象进行赋值 线程安全
pubic class Singleton {
private Singleton instance = null;
static {
instance = new Singleton;
}
private Singleton () {};
public static Singleton getInstance() {
return this.instance;
}
}
但是 上面的通过类加载机制完成的创建单例模式 有着一点小问题
1.如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例.假定不是远端存取,
例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例.
所以 针对此问题 可以用下面方法解决
// 通过 类的权限定名称 返回其Class类 里面确保了 使用同一个 类加载,用来保证加载时,创建用一单例
private static Class getClass(String classname) throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader == null)
classLoader = Singleton.class.getClassLoader();
return (classLoader.loadClass(classname));
}
}
2.如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原.不管怎样,如果你序列化一个单例类的对象,
接下来复原多个那个对象,那你就会有多个单例类的实例
解决方案:
// 通过 他的 readResolve方法 保证实现一个单例
public class Singleton implements java.io.Serializable {
public static Singleton INSTANCE = new Singleton();
protected Singleton() {
}
private Object readResolve() {
return INSTANCE;
}
}
总结下来 双重判定锁的创建单例以及静态内部类的创建单例,还是效率挺高的,但是为了安全,为了防止反射,推荐大家使用枚举方法。