java性能优化--代理--单例

单例模式是一种对象创建模式,用于生产对象的一个具体实例,它可以确保系统中一个类只产生一个实例。该模式能带来两大好处:

    1. 对于频繁使用的对象,可以省略创建对象所花费的时间。

    2. 由于new的次数减少,因而对系统内存的使用频率也会降低,这将减轻GC压力、缩短gc停顿时间

单例模式的核心,在于通过一个接口返回唯一的对象实例。一个简单的单例实现:


public class Singleton {
	private Singleton(){
		System.out.println("create a Singleton");
	}
	
	private static Singleton instance = new Singleton();

	public static Singleton getInstance(){ return instance;
	}
}
  首先,单例必须有一个private访问级别的构造函数,只有这样,才能确保单例不会在系统中的其他代码内被实例化;其次,instance成员变量和getInstance()的方法必须是static的



这种实现方式很简单并且还是十分可靠的,它唯一的不足就是无法对instance进行延迟加载,实现延迟的代码为

public class LazySingleton {
	private LazySingleton(){
		System.out.println("LazySingleton is create");
	}
	
	private static LazySingleton instance = null;
	public static synchronized LazySingleton getInstance(){
		if(instance == null){
			instance = new LazySingleton();
		}
		return instance;
	}
}
首先,对于静态变量instance初始值赋予null,确保系统启动时没有额外的负载;
其次,get方法必须是同步的,否则在多线程请扩下,可能会多次创建!
但是由于引入了同步,其性能会和第一种情况差了很多,所以这种降低了系统性能的做法,是有点不可取的,所以我们对其进行改进:
public class StaticSingleton {


 private StaticSingleton(){
 System.out.println("create a StaticSingleton");
 }
 
 private static class SingletonHolder{
 private static StaticSingleton instance = new StaticSingleton();
 }
 
 public static StaticSingleton getInstance(){
 return SingletonHolder.instance;
 }
}
这个实现中,单例模式使用内部类来维护实例,当StaticSingleton被加载时,其内部类不会初始化,故可以在调用getInstance()方法调用,才会加载SingletonHolder.同时,由于实例的建立实在类加载时完成,故天生对多线程友好
通常情况下上面的代码可以确保系统中唯一实例。但是仍有意外情况,比如利用反射,这种情况我们不讨论,我们讨论一种合理的方法:
public class SerSingleton implements Serializable {


 String name;
 private SerSingleton(){
 System.out.println("create a SerSingleton");
 name = "SerSingleton";
 }
 
 private static SerSingleton instance = new SerSingleton();
 public static SerSingleton getInstance(){
 return instance;
 }
 
 public static void createString(){
 System.out.println("createString in Singleton");
 }
 
//	private Object readResolve(){
//		return instance;
//	}
}
// TODO Auto-generated method stub
 SerSingleton s1 = null;
 SerSingleton s  = SerSingleton.getInstance();
 //先将实例串行化到文件
 FileOutputStream fos = new FileOutputStream("s.txt");
 ObjectOutputStream oos = new ObjectOutputStream(fos);
 oos.writeObject(s);
 oos.flush();
 oos.close();
 
 //从文件读出
 FileInputStream fis = new FileInputStream("s.txt");
 ObjectInputStream ois = new ObjectInputStream(fis);
 s1= (SerSingleton) ois.readObject();
//		Assert.assertEquals(s,s1);
 System.out.println(s.equals(s1));
如果在第一段代码中去掉readResovle(),那么测试代码(经过串行化和反串行化)后,s和s1指向了不同的实例;加上之后便解决该问题。
即使构造函数是私有的,可序列化工具依然可以通过特殊的途径去创建类的一个新的实例。序列化操作提供了一个很特别的钩子(hook)-类中具有一个私有的被实例化的方法readresolve(),这个方法可以确保类的开发人员在序列化将会返回怎样的object上具有发言权。足够奇怪的,readresolve()并不是静态的,但是在序列化创建实例的时候被引用。


你可能感兴趣的:(java性能优化--代理--单例)