Java实现单例模式

Posted by Chejdj Blog on March 2, 2018

在面试的时候应该经常会问道的一种题型,使用某种语言实现单例模式,这里就记录一下常见的几种方法。

单例模式:实现一个只能生成一个实例的类。

方法一

1
2
3
4
5
6
7
8
9
public Singleton{
   private Singleton instance=null;
   private Singleton(){}
   public static Singleton getInstance{
   if(instance==null)
  	instance = new Singleton();
   	return instance;
  }
}

缺点:只能在单线程的环境工作,如果两个线程同时运行到if()判断,则会创建两个实例

方法二

1
2
3
4
5
6
7
8
9
public class singleton {
   private static singleton instance=null;
   private singleton(){}
   public synchronized static singleton getInstance(){
   	if(instance==null)
   	  instance=new singleton();
 	  return instance;
	}
}

加同步锁的方法,可以在多线程的情况下实现单例模式,但是执行的效率并不高,每一次执行getInstance都会尝试加锁,而加锁也是一个比较耗时的操作。

方法三

1
2
3
4
5
6
7
8
9
10
11
12
13
public class singleton1{
  private volatile static singleton1 instance =null;
  private singleton1(){};
  public static singleton1 getInstacne(){
   	if(instance==null){
   	  synchronized(singleton1.class){
   	  if(instance==null)
   	  instance=new singleton1();
   	    }
  	  }
   	  return instance;
   	}
} //加入volatile防止指令重排序的问题

目前认为比较好的方法,只有当尝试创建实例的时候,才加锁

方法四

1
2
3
4
5
6
7
8
9
public class singleton2{
  private singleton2(){}
  public static singleton2 getInstance(){
   return singleholder.single;
  }
  private static class singleholder{
   private static sinleton2 single=new singleton2(); 
 }
}
1
2
3
4
5
6
7
public class singleton2{
  private static singleton2 single=new singleton2();
  private singleton2(){}
  public static singleton2 getInstance(){
   return single;
  }
}

方法四中给了两种方法,但是第一种更好(只是相差比较细微的差别),他们都没有加锁操作效率更高,内部类只加载一次。他们都是在类加载的时候,创建singleton2实例。
第一种只有明显的调用 singleholder.single 才会创建实例,因为singleholder类加载了。
第二种一般也是调用singleton2.getInstance才会实现singleton2类加载从而创建singleton2的实例,但是如果该类有其他的静态方法调用(调用该类的静态方法,你并不想实现实例),也会创建singleton2实例。

方法五

1
2
3
4
public enum singleton3{
INSTANCE;
public void method(){...}
}

使用枚举实现单例,出现在《effective Java》中(后悔买了,看不懂),说是能够绝对的防止多次实例化,即使是在面对复杂的序列化或则反射攻击的时候。书上说单元素的枚举类型是实现单例模式的最佳方法。
我个人比较推崇方法三和四。