饿汉式单例:

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;
    }

}

 

双重检查锁实现单例二:

23模式之: 单例模式demo_延迟加载

 

待测试类:

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

 

性能图:

23模式之: 单例模式demo_java_02

 

 

谢谢声明出处!