单例模式
核心:
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
常用场景:
1. windows 系统 任务管理器、回收站
2. 网站计数器
3. 配置文件
4. 操作系统的文件系统
5. spring 中的 bean 默认是单例
...
优点:
减少系统开销
常见的五种单例模式
-
主要
- 饿汉式(线程安全、调用效率高。但是,不能延时加载)
- 懒汉式(线程安全、调用效率不高。但是可以延时加载)
-
其它
- 双重检测锁式(由于JVM底层内部模型原因,偶尔会出现问题。不建议使用)
- 静态内部类式(线程安全、调用效率高。可以延时加载)
- 枚举单例(线程安全、调用效率不高。但是可以延时加载,天然防止反射、反序列化漏洞)
问题
- 反射破解(不包含枚举类)以上几种方式(可以在构造方法中手动抛出异常空值)
1. 饿汉式
/*
* 饿汉式实现单例模式
*
* 1. 构造方法私有化
* 2. 类初始化时,立即加载(希望延时加载,即懒汉式)
*/
public class Singleton {
// 天然的线程安全,调用效率高。
private static Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
}
2. 懒汉式
/*
* 懒汉式 单例模式
*
* 延迟加载,真正用的时候才加载
*/
public class SingletonLazy implements Serializable {
private static SingletonLazy singleton;
private SingletonLazy() {
// 防止反射破解单例模式
// if (singleton != null) {
// throw new RuntimeException();
// }
}
// 每次调用都要同步
// 资源利用率高,并发效率低
public static synchronized SingletonLazy getInstance() {
if (singleton == null)
singleton = new SingletonLazy();
return singleton;
}
// 反序列化时,如果定义了 readResolve 方法则直接返回此方法返回的对象,不需要单独再创建对象
// private Object readResolve() throws ObjectStreamException {
// return singleton;
// }
}
3. 双重检验锁
/*
* 双重检测锁模式
*/
public class SingletonLock {
private static SingletonLock singleton;
private SingletonLock() {
}
public static SingletonLock getInstance() {
if (singleton == null) {
SingletonLock sl;
synchronized (SingletonLock.class) {
sl = singleton;
if (sl == null) {
synchronized (SingletonLock.class) {
if (sl == null) {
sl = new SingletonLock();
}
}
singleton = sl;
}
}
}
return singleton;
}
}
4. 静态内部类
/*
* 静态内部类实现单例模式
*
* 线程安全并且懒加载
*/
public class SingletonInner {
private SingletonInner() {
}
private static class SingletonInnerClass {
private static final SingletonInner instance = new SingletonInner();
}
public static SingletonInner getInstance() {
return SingletonInnerClass.instance;
}
}
5. 枚举类
/*
* 枚举实现单例
*/
public enum SingletonEnum {
// 枚举元素,本身就是单例对象,没有懒加载
INSTANCE;
public void getInstance() {
}
}
测试反射、反序列化
/**
* 测试反射、反序列化破解单例
*
* @author apple
*/
public class Main {
public static void main(String[] args) throws Exception {
SingletonLazy instance1 = SingletonLazy.getInstance();
// SingletonLazy instance2 = SingletonLazy.getInstance();
//
System.out.println(instance1);
// System.out.println(instance2);
//
// // 通过反射获取私有构造器
// Class<SingletonLazy> clz = (Class<SingletonLazy>)
// Class.forName("com.beng.design.singleton.SingletonLazy");
// Constructor<SingletonLazy> cons = clz.getDeclaredConstructor(null);
// cons.setAccessible(true);
// SingletonLazy instance3 = cons.newInstance();
// SingletonLazy instance4 = cons.newInstance();
//
// System.out.println(instance3);
// System.out.println(instance4);
// 通过序列化的方式构造多个对象
// FileOutputStream fos = new FileOutputStream("a.txt");
// ObjectOutputStream oos = new ObjectOutputStream(fos);
// oos.writeObject(instance1);
// oos.close();
// fos.close();
// System.out.println("end");
ObjectInputStream oin = new ObjectInputStream(new FileInputStream("a.txt"));
SingletonLazy instance = (SingletonLazy) oin.readObject();
System.out.println(instance);
}
}
测试单例模式效率
/**
* 测试创建单例模式 效率
*
* @author apple
*/
public class Test {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
int nthreads = 10;
final CountDownLatch count = new CountDownLatch(nthreads);
for (int j = 0; j < nthreads; ++j) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000000; ++i) {
Object obj = SingletonEnum.INSTANCE;
}
count.countDown();
}
}).start();
}
long end = System.currentTimeMillis();
count.await(); // main 线程阻塞,直到count=0
System.out.println("总耗时:" + (end - start));
}
}