话不多说,先上代码
public class LazySingleton {
private LazySingleton (){
if(LazyHandler.LAZY!=null){
throw new RuntimeException("单例模式不允许出现多例");
}
}
private static class LazyHandler{
private static final LazySingleton LAZY = new LazySingleton();
}
public static final LazySingleton getInstance() {
return LazyHandler.LAZY;
}
}
这个便是查了好多资料后总结出来的终极懒加载单例模式;
先一步一步说一下懒加载单例模式的一步步进化写法,在过程中了解终极写法的优势
最初版
public class LazySingleton {
private LazySingleton (){}
private static LazySingleton lazySingleton;
public static LazySingleton getInstance() {
if(lazySingleton!=null){
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
最初版代码思想就是单例模式的基本思想操作:
1、构造函数私有化;2、创建一个私有的静态属性保证全代码世界只有这一个单例;3、创建一个静态的getInstance来实现外部调用;
缺点:因为是最初版,缺点还是很好找,线程不安全,一个线程进入if,另一个也进入if,那么就出现了俩不同实例。
进阶版
public class LazySingleton {
private LazySingleton (){}
private static LazySingleton lazySingleton;
public static LazySingleton getInstance() {
if(lazySingleton!=null) {
synchronized (LazySingleton.class) {
if(lazySingleton!=null){
lazySingleton = new LazySingleton();
}
}
}
return lazySingleton;
}
}
既然是进阶版,自然是解决掉了基础版的线程安全问题了,当然也有不同的写法,比如说 synchronized 直接加在getInstance方法上,但是思路就是一条,那就是加锁,处理掉线程安全问题。
缺点:如果有点小缺点的话,就是有可能在锁的时候,造成阻塞,影响性能了。毕竟第一个在用,还把门锁上了,第二个自然就只能再门外瑟瑟发抖等前人用完。
终极版
public class LazySingleton {
private LazySingleton (){
if(LazyHandler.LAZY!=null){
throw new RuntimeException("单例模式不允许出现多例");
}
}
private static class LazyHandler{
private static final LazySingleton LAZY = new LazySingleton();
}
public static final LazySingleton getInstance() {
return LazyHandler.LAZY;
}
}
终极版目前来说就是最优解了。原理解析如下(其实就是用另类的饿汉式解决了懒汉式)
1、怎么实现了单例:单例通过静态实现,原理相当于是结合了内部类和饿汉式。
2、怎么实现了懒汉式:在java的类加载时候,内部类是不加在的,直到使用内部类的时候才会加载并初始化
3、怎么解决掉多线程的安全问题:静态类的初始化是又jvm加载类时候初始化完成的,不会有多线程的问题,原理等同于饿汉式
4、为什么私有的构造函数要加一个判定:如果通过反射的形式生成的LazySingleton ,破坏了单例,所以在私有构造函数上保证只能一个。
此处介绍两种 基本没见过也没什么用的破坏单例方法
一、反射破坏
public static void main(String[] args) {
try {
LazySingleton hsl = LazySingleton.getInstance();
Class cla = LazySingleton.class;
Constructor con = cla.getDeclaredConstructor();
con.setAccessible(true);
Object o = (LazySingleton)con.newInstance();
System.out.println(o);
System.out.println(hsl);
System.out.println(o == hsl);
} catch (Exception e) {
e.printStackTrace();
}
}
通过反射获取私有构造函数,给权限,然后强行破坏单例,生成多例。在终极版里边通过私有构造抛异常解决。
二、文件写入写出破坏
public static void main(String[] args) {
LazySingleton h = null;
LazySingleton h1 = LazySingleton.getInstance();
FileOutputStream fos = null;
try {
fos = new FileOutputStream("LazySingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(h1);
oos.flush();
oos.close();
fos.close();
FileInputStream fis = new FileInputStream("LazySingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
h = (LazySingleton) ois.readObject();
ois.close();
fis.close();
System.out.println(h);
System.out.println(h1);
System.out.println(h == h1);
} catch (Exception e) {
e.printStackTrace();
}
}
如果要使用这个测试记得实现 Serializable 接口 ,看到这里 运行一下就会发现其实 最终版是没有应对这个的方法的,解决的代码如下
最最终极补全加强版
public class LazySingleton implements Serializable {
private LazySingleton (){
if(LazyHandler.LAZY!=null){
throw new RuntimeException("单例模式不允许出现多例");
}
}
private static class LazyHandler{
private static final LazySingleton LAZY = new LazySingleton();
}
private Object readResolve(){
return LazyHandler.LAZY;
}
public static final LazySingleton getInstance() {
return LazyHandler.LAZY;
}
}
多了一个readResolve的方法,文件的写入写出方法是 readObject 方法,跟一下代码就发现,如果类有readResolve 方法,即使实例化了对象也会调用readResolve 覆盖掉新生成的对象,实现单例。