饿汉式单例:
package singleton_mode; /** * 饿汉式单例模式 */ public class SingletonDemo1 { /** * 步骤2: 类加载时, 立即创建此对象, 此时期是天然线程安全的 */ private static SingletonDemo1 instance = new SingletonDemo1(); /** * 步骤1: 私有化构造器 */ private SingletonDemo1() { } /** * 步骤3: 如果用不上它(各种原因类被加载了), 内存中已存在了该对象, 造成资源浪费 */ public static SingletonDemo1 getInstance(){ return instance; } }
懒汉式单例:
package singleton_mode; /** * 懒汉式单例模式, 延迟加载, 懒加载 */ public class SingletonDemo2 { /** * 步骤2: 类加载时, 初始化为null; */ private static SingletonDemo2 instance; /** * 步骤1: 私有化构造器 */ private SingletonDemo2() { } /** * 步骤3: 只有第一次调用时, 才创建此对象, 资源利用率高了, 但并发下效率低 */ public static synchronized SingletonDemo2 getInstance(){// if (instance == null) {//如果方法不加锁, 会有多个线程走到这里, 然后此对象创建多次 instance = new SingletonDemo2(); } return instance; } }
静态内部类实现单例:
package singleton_mode; /** * 静态内部类实现单例模式, 延迟加载, 优于懒汉式 */ public class SingletonDemo3 { /** * 步骤3: 只有真正调getInstance(), 才初始化此, 类加载时期由jvm保证了线程安全 */ private static class SingletonClassInstance { private static final SingletonDemo3 instance = new SingletonDemo3(); } /** * 步骤1: 私有化构造器 */ private SingletonDemo3() { } /** * 步骤2: 并发下高效调用, 创建时线程安全, 又有延迟加载 */ public static SingletonDemo3 getInstance(){ return SingletonClassInstance.instance; } }
枚举实现单例:
package singleton_mode; /** * 枚举实现单例模式 * 优点: 实现简单, 枚举本身就是单例模式, * 避免了通过反序列化和反射(即使构造私有了,可以通过反射去调)的漏洞创建新的对象 * 缺点: 不是延迟加载, 真正用的时候, 内存中可能已有此对象 */ public enum SingletonDemo4 { /** * 定义一个枚举元素, 调用时类本身就成为单例对象, 由JVM从根本上提供保障. */ INSTANCE; /** * 添加自己需要的操作 */ public void singletonOperation(){ //... } }
双重检查锁实现单例一:
package singleton_mode; /** * 双重检查锁实现单例模式, 延迟加载, 高效调用, 需要Java1.5及++ */ public class SingletonDemo5 { /** * 步骤2: volatile关键字确保: 当instance变量被初始化成SingletonDemo5实例时, 多个线程正确的处理instance变量 * 在1.4及更早版本的Java中, 许多JVM对于volatile关键字的实现会导致双重检查加锁的失效, 使用此模式请确保Java1.5及++ */ private volatile static SingletonDemo5 instance; /** * 步骤1: 私有化构造器 */ private SingletonDemo5() { } /** * 步骤3: 仅第一初始化时进行同步, 并发下高效调用, 创建时线程安全, 又有延迟加载 */ public static SingletonDemo5 getInstance(){ if (instance == null) {//检查实例, 如果不存在, 就进入同步区块 synchronized (SingletonDemo5.class) { if (instance == null) {//进入同步区块后, 再检查一次, 如果仍是null, 才创建实例 instance = new SingletonDemo5(); } } } return instance; } }
双重检查锁实现单例二:
待测试类:
package singleton_mode; import java.io.ObjectStreamException; import java.io.Serializable; /** * 待测试反序列化和反射漏洞类 */ public class SingletonBack implements Serializable { private static final long serialVersionUID = -8392310490920809266L; private static SingletonBack instance = new SingletonBack(); /** * 防止反射: 通过反射new时对象已存在将失败 */ private SingletonBack() { // if (instance != null) { // throw new RuntimeException(); // } } public static SingletonBack getInstance(){ return instance; } /** * 防止反序列化: 此方法是回调方法 * 反序列化时, 如果定义了readResolve方法, 则直接返回此方法指定的对象, 替换序列化后的对象, 比喻偷天换日吧 * @throws ObjectStreamException */ // private Object readResolve() throws ObjectStreamException { // return instance; // } }
测试类:
package singleton_mode; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; import java.util.concurrent.CountDownLatch; public class Client { /** * 测试是否单例 */ public void test01(){ SingletonDemo1 s11 = SingletonDemo1.getInstance(); SingletonDemo1 s12 = SingletonDemo1.getInstance(); SingletonDemo2 s21 = SingletonDemo2.getInstance(); SingletonDemo2 s22 = SingletonDemo2.getInstance(); SingletonDemo3 s31 = SingletonDemo3.getInstance(); SingletonDemo3 s32 = SingletonDemo3.getInstance(); SingletonDemo4 s41 = SingletonDemo4.INSTANCE; SingletonDemo4 s42 = SingletonDemo4.INSTANCE; SingletonDemo5 s51 = SingletonDemo5.getInstance(); SingletonDemo5 s52 = SingletonDemo5.getInstance(); System.out.println(s11 == s12 && s21 == s22 && s31 == s32 && s41 == s42 && s51 == s52); } /** * 测试反射破解单例模式, 通过反射的方式直接调用私有构造器 * @throws Exception */ public void test02() throws Exception { Class<SingletonBack> class1 = (Class<SingletonBack>) Class.forName("singleton_mode.SingletonBack"); Constructor<SingletonBack> co = class1.getConstructor(null);//获得无参构造器 co.setAccessible(true);//设置可访问, 原先私有的 SingletonBack s1 = co.newInstance(); SingletonBack s2 = co.newInstance(); System.out.println(s1); System.out.println(s2); } /** * 测试反序列化破解单例模式, 通过反序列化的方式构造多个对象 * @throws Exception */ public void test03() throws Exception { SingletonBack s1 = SingletonBack.getInstance(); System.out.println(s1); //字节流形式 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos);//创建object输出流(传入字节数组输出流) oos.writeObject(s1);//序列化对象 byte[] bytes = bos.toByteArray();//从字节数组输出流中, 获得字节数组 oos.close(); ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bis);//创建object输入流(传入字节数组输入流) SingletonBack ss1 = (SingletonBack) ois.readObject();//反序列化对象 ois.close(); System.out.println(ss1); //文件流形式 // ObjectOutputStream oos2 = new ObjectOutputStream(new FileOutputStream(new File("D:\\file1"))); // oos2.writeObject(s1); // oos2.close(); // // ObjectInputStream ois2 = new ObjectInputStream(new FileInputStream(new File("D:\\file1"))); // SingletonBack ss1 = (SingletonBack) ois2.readObject(); // ois2.close(); // System.out.println(ss1); } /** * 测试多线程环境下几种创建单例模式的效率 */ public static void main(String[] args) throws Exception { Client client = new Client(); client.test01(); //client.test02(); client.test03(); int threadNum = 10; final CountDownLatch countDownLatch = new CountDownLatch(threadNum); long start = System.currentTimeMillis(); for (int i = 0; i < threadNum; i++) { new Thread(new Runnable() { //内部类不能直接使用外部的局部变量, 因为内外生命周期是不一样的, 比方外部方法执行完变量销毁, 内部类还在执行用到它 @Override public void run() { for (int i = 0; i < 100000; i++) { Object o = SingletonDemo1.getInstance();//测试饿汉式, 需要自己改 } countDownLatch.countDown(); } }).start();//main线程只是new并启动了10个线程, 然后继续向下走, 不管10个线程是否走完 } countDownLatch.await();//main线程阻塞, 直到计数器为0, 才会继续往下执行, 底层while循环检测 long end = System.currentTimeMillis(); System.out.println("总耗时: " + (end - start)); } }
执行结果:
true singleton_mode.SingletonBack@1db9742 singleton_mode.SingletonBack@9e54c2 总耗时: 0
性能图:
谢谢声明出处!