单例模式确保某个类只有一个实例,并且自行实例化,并向整个系统提供这个实例
立即加载/饿汉模式
调用方法前,实例已经被创建
public class Single1 {
//立即加载、饿汉模式
private static Single1 single1=new Single1();
//私有构造
//定义为private,避免在外部被实例化
private Single1() {}
//静态工厂方法
public static Single1 getSingle() {
return single1;
}
}
public class MyThread extends Thread{
public void run() {
System.out.println(Single1.getSingle().hashCode());
}
}
public class Test {
public static void main(String[] args) {
MyThread thread1=new MyThread();
MyThread thread2=new MyThread();
MyThread thread3=new MyThread();
thread1.start();
thread2.start();
thread3.start();
}
}
延迟加载/懒汉模式
调用方法时,实例才被创建
public class Single2 {
private static Single2 single2;
private Single2() {};
public static Single2 getSingle() {
if(single2==null)
single2=new Single2();
return single2;
}
}
以上代码,在多线程下,将会创建多个实例,与单例模式初衷相背离,是完全错误的。
如果稍加修改,就能看出端倪
public class Single2 {
private static Single2 single2;
private Single2() {};
public static Single2 getSingle() {
if(single2==null){
//这里让线程睡眠
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
single2=new Single2();
}
return single2;
}
}
延迟加载/懒汉模式的解决方案
1、使用 synchronized 同步标记
public class Single3 {
private static Single3 single3;
private Single3() {};
synchronized public static Single3 getSingle() {
if(single3==null)
single3=new Single3();
return single3;
}
}
虽然保证了同一个实例,但是效率低下。
2、使用双重检查锁
public class Single4 {
private static Single4 single4;
private Single4() {};
public static Single4 getSingle() {
if(single4==null) {
synchronized (Single4.class) {
if(single4==null)
single4=new Single4();
}
}
return single4;
}
}
使用双重检查锁,即保证了不会出现多个实例,又保证了线程执行的效率
使用静态内置类实现
public class Single5 {
private static class Single5In{
private static Single5 single5=new Single5();
}
private Single5() {}
public static Single5 getSingle() {
return Single5In.single5;
}
}
序列化问题
如果遇到序列化对象时,使用默认的方式运行得到的结果还是多实例
public class Single5 implements Serializable{
private static class Single5In{
private static Single5 single5=new Single5();
}
private Single5() {}
public static Single5 getSingle() {
return Single5In.single5;
}
// private Object readResolve() {
// return Single5In.single5;
// }
}
public class Test {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream("tempfile"));
objectOutputStream.writeObject(Single5.getSingle());
ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream("tempfile"));
System.out.println(Single5.getSingle()==(Single5)objectInputStream.readObject());
objectOutputStream.close();
objectInputStream.close();
}
}
得到的结果为false,即序列化破坏了单例。
原因是序列化会通过反射调用无参的构造方法创建一个新的对象
那么如何解决呢
事实上,将上面代码中的注释部分恢复即可
使用static静态代码块
public class Single6 {
private static Single6 single6=null;
private Single6(){};
static {
single6=new Single6();
}
public static Single6 getSingle() {
return single6;
}
}
使用enum枚举
使用枚举类时,构造方法会被自动调用,因此可以用这一特性来实现单例模式。
public class Single7 {
public enum MySingle {
SINGLE;
private Single7 single7;
private MySingle() {
single7=new Single7();
}
public Single7 getSingle() {
return single7;
}
}
private Single7 () {};
public static Single7 getSingle() {
return MySingle.SINGLE.getSingle();
}
}