单例设计模式
作用:
保证一个类只有一个对象,并且提供一个访问该实例的全局访问点
常见场景:
- windows中任务管理器
- Windows中回收站
- 网站计数器
- 日志应用
- 数据库连接池
- 操作系统的文件系统
- Application(Servlet)
- Spring中的每个bean默认是单例
- SpringMVC中控制器也是单例的
单例模式的优点:
单例模式只生成一个实例,减少系统性能的开销,当一个对象产生多的资源时,读取配置,产生其他对象时,需要在应用启动时
直接产生一个单例对象,永久保存在内存中的方式解决。
单例模式的主要分类:
- 饿汉单例(线程安全,调用率高,不能够延迟加载);
- 懒汉单例(线程安全,调用效率不高,可以延迟加载);
- 双重检测锁式(由于JVM底层内部模型原因,偶尔出问题,不建议使用);
- 静态内部类式(线程安全,调用效率高,可以延迟加载);
- 枚举单例(线程安全,调用效率高,不能延迟加载);
饿汉
package com.wangpx.design;
/**
* 饿汉式单例模式
* @author wangpx
*
*/
public class Singleton {
//类初始化时立即加载,线程安全
private static Singleton singleton = new Singleton();
private Singleton() {};
//方法没有同步,效率高
public static Singleton getInstance() {
return singleton;
}
}
懒汉
package com.wangpx.design;
/**
*
* 懒汉单例模式
* 延迟加载,真正用的时候才使用 对象懒加载
* @author wangpx
*/
public class Singleton2 {
private static Singleton2 singleton2;
private Singleton2 () {};
//同步方法 并发效率低
public static synchronized Singleton2 getInstance() {
if (singleton2 == null) {
singleton2 = new Singleton2();
}
return singleton2;
}
}
双重检查锁
package com.wangpx.design;
/**
*
* 双重检查锁单例模式
* 提高了执行效率,不必每次获取对象时都进行同步,只有第一次才同步创建后就没有必要了
* @author wangpx
*/
public class Singleton3 {
private static Singleton3 singleton3 = null;
private Singleton3() {};
public static synchronized Singleton3 getInstance() {
if (singleton3 == null) {
Singleton3 singleton31;
synchronized (Singleton3.class) {
singleton31 = singleton3;
if (singleton31 == null) {
synchronized (Singleton3.class) {
if (singleton31 == null) {
singleton31 = new Singleton3();
}
}
singleton3 = singleton31;
}
}
}
return singleton3;
}
}
静态内部类
package com.wangpx.design;
/**
*
* 静态内部类单例模式
* @author wangpx
*/
public class Singleton4 {
private Singleton4() {}
/**
* 内部类没有static属性,不会像饿汉模式类初始化时加载对象
* 只有调用getInstance()才会加载静态内部类
* 线程安全,instance是 static final类型,保证内存中只有一个实例,只能赋值一次
* 并发高效调用 延迟加载
*/
private static class SingletonClassInstance {
private static final Singleton4 instance = new Singleton4();
}
public static Singleton4 getInstance() {
return SingletonClassInstance.instance;
}
}
枚举类型
package com.wangpx.design;
/**
*
* 枚举单例模式
* 没有延迟加载
* 防止反射和反序列化漏洞
* @author wangpx
*/
public enum Singleton5 {
/**
* 定义一个枚举元素,代表一个Singleton实例,本身单例
*/
INSTANCE;
public void singletonOperation() {
//功能实现
}
}
反射和反序列化漏洞的解决方案
package com.wangpx.design;
import java.io.ObjectStreamException;
import java.io.Serializable;
/**
*
* 防止反射和反序列化漏洞
* @author wangpx
*/
public class Singleton6 implements Serializable {
private static Singleton6 singleton6;
private Singleton6() {
if (singleton6 != null) {
throw new RuntimeException();
}
};
//同步方法 并发效率低
public static synchronized Singleton6 getInstance() {
if (singleton6 == null) {
singleton6 = new Singleton6();
}
return singleton6;
}
/**
* 反序列化时,直接调用readResolve()方法,返回此方法指定对象,不需要再次创建新对象
* @return
* @throws ObjectStreamException
*/
private Object readResolve () throws ObjectStreamException {
return singleton6;
}
}
package com.wangpx.test;
import com.wangpx.design.Singleton6;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test2 implements Serializable {
public static void main(String[] args) {
Singleton6 instance = Singleton6.getInstance();
System.out.println(instance);
/**
* 通过反射的方式破解单例模式调用私有构造器
*/
/* try {
Class<Singleton6> clazz = (Class<Singleton6>) Class.forName("com.wangpx.design.Singleton6");
Constructor<Singleton6> constructor = clazz.getDeclaredConstructor(null);
constructor.setAccessible(true);
Singleton6 s1 = constructor.newInstance();
Singleton6 s2 = constructor.newInstance();
System.out.println(s1 == s2);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}*/
/**
* 通过反序列化构建多个对象
*
*/
FileOutputStream fos =null ;
ObjectOutputStream oos = null;
try {
fos= new FileOutputStream("d:/wangpx.log");
oos = new ObjectOutputStream(fos);
oos.writeObject(instance);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fos.close();
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("d:/wangpx.log"));
Singleton6 singleton6 = (Singleton6) ois.readObject();
System.out.println(singleton6);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
测试执行效率
package com.wangpx.test;
import com.wangpx.design.*;
import java.util.concurrent.CountDownLatch;
/**
* 测试五种单例模式的效率
*/
public class Test3 {
public static void main(String[] args) throws InterruptedException {
/**
* 饿汉
*/
long start = System.currentTimeMillis();
int threadNum = 10;
final CountDownLatch countDownLatch = new CountDownLatch(threadNum);
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000000; i++) {
//
// Singleton4 instance = Singleton4.getInstance();
Object o= Singleton5.INSTANCE;
}
countDownLatch.countDown();
}
}).start();
}
//main线程阻塞,直到计数器为0,才会往下执行
countDownLatch.await();
long end = System.currentTimeMillis();
System.out.println("总耗时:"+(end-start)+"ms");
}
}