立即加载就是使用类的时候已经将对象创建完毕,常见的实现方法是直接new 实例化。
创建MyObject.java:
public class MyObject { private static MyObject myObject = new MyObject(); private MyObject(){ } public static MyObject getInstance(){ return myObject; } }创建MyThread.java:
public class MyThread extends Thread { @Override public void run() { System.out.println(MyObject.getInstance().hashCode()); } }创建测试类Run.java:
public class Run { public static void main(String[] args) { MyThread a1 = new MyThread(); MyThread a2 = new MyThread(); MyThread a3 = new MyThread(); a1.start(); a2.start(); a3.start(); } }最后的运行结果如下,可见是同一个对象,即实现了立即加载型单例模式。
1097280301 1097280301 1097280301延迟加载就是在调用get()方法时实例才被创建,常见的实现方法就是在get()方法中进行new实例化,延迟加载也被称作"懒汉模式" 修改MyObject类如下:
public class MyObject { private static MyObject myObject; private MyObject(){ } public static MyObject getInstance(){ //延迟加载 if(myObject == null){ myObject = new MyObject(); } return myObject; } }最后的运行结果如下,能够看到多线程环境中并不是同一个单例
980142845 1553816201 418194775 上面的延迟加载/"饿汉模式"在多线程的环境中不能实现单例,解决方案如下: 修改MyObject类如下: public class MyObject { private static MyObject myObject; private MyObject(){ } synchronized public static MyObject getInstance(){ //延迟加载 if(myObject == null) { myObject = new MyObject(); } return myObject; } } 1553816201 1553816201 1553816201最后的运行结果虽然实现了是取的同一个实例,但是效率却非常的低,尝试同步代码块和直接同步方法是一样的,效率也提高不了多少,最后采用DCL双检查锁机制
修改MyObject类如下:
public class MyObject { private static MyObject myObject; private MyObject(){ } public static MyObject getInstance(){ //延迟加载 if(myObject == null) { synchronized(MyObject.class){ if(myObject == null){ myObject = new MyObject(); } } } return myObject; } }最后的运行结果仍然是单例的,且提升了效率
1230761675 1230761675 1230761675DCL可以解决多线程单例模式的非线程安全问题,当然其他的办法也能达到同样的效果 修改MyObject类如下:
public class MyObject { private MyObject(){ } public static MyObject getInstance(){ return MyObjectHandler.myObject; } private static class MyObjectHandler{ private static MyObject myObject = new MyObject(); } }最后的运行结果如下:
964250181 964250181 964250181静态内置类可以达到线程安全问题,但如果遇到序列化对象时,使用默认的方式运行得到的结果还是多例的,解决办法就是在反序列化中使用readResolve()方法
public class MyObject implements Serializable { private MyObject(){ } public static MyObject getInstance(){ return MyObjectHandler.myObject; } private static class MyObjectHandler{ private static MyObject myObject = new MyObject(); } protected Object readResolve(){ return MyObjectHandler.myObject; } }创建测试类:
public class Run { public static void main(String[] args) { try { MyObject myObject = MyObject.getInstance(); FileOutputStream fosRef = new FileOutputStream(new File("MyObject.txt")); ObjectOutputStream fo = new ObjectOutputStream(fosRef); fo.writeObject(myObject); fo.close(); fosRef.close(); System.out.println(myObject.hashCode()); FileInputStream flsRef = new FileInputStream("MyObject.txt"); ObjectInputStream ioRef = new ObjectInputStream(flsRef); MyObject m = (MyObject)ioRef.readObject(); ioRef.close(); flsRef.close(); System.out.println(m.hashCode()); }catch (Exception e){ e.printStackTrace(); } } }最后的运行结果显示两者是单例的。
325040804 325040804修改MyObject类如下:
public class MyObject { private static MyObject instan = null; static { instan = new MyObject(); } private MyObject(){ } public static MyObject getInstance(){ return instan; } }创建Run.java类:
public class Run { public static void main(String[] args) { MyThread a1 = new MyThread(); MyThread a2 = new MyThread(); MyThread a3 = new MyThread(); a1.start(); a2.start(); a3.start(); } }最后的运行结果如下:
2049367639 2049367639 2049367639枚举Enum和静态代码块的特性相似,在使用枚举类时,构造方法会被自动调用。即把MyObject类定义为枚举类