1、单例模式
单例模式的优点:
单例模式可以保证内存里只有一个实例,减少了内存的开销。
可以避免对资源的多重占用。
单例模式设置全局访问点,可以优化和共享资源的访问。
单例模式的缺点:
单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。
应用场景:
1、需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。
2、某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。
3、某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
4、某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
5、频繁访问数据库或文件的对象。
6、对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套。
7、当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。
实现方式:
懒汉式(每次构建对象都需要同步,性能消耗大)
public class SingletonModelTest {
private static SingletonModelTest singletonModelTest = null;
private SingletonModelTest(){
}
public synchronized static SingletonModelTest getInstance() {
if (singletonModelTest == null) {
singletonModelTest = new SingletonModelTest();
}
return singletonModelTest;
}
}
饿汉式(线程不安全)
public class SingletonModelTest {
private static SingletonModelTest singletonModelTest = new SingletonModelTest();
private SingletonModelTest(){
}
public synchronized static SingletonModelTest getInstance() {
return singletonModelTest;
}
}
双重校验(写法复杂)
// 双重校验
private volatile static SingletonModelTest singletonModelTest = null;
private SingletonModelTest(){}
public static SingletonModelTest getInstance() {
if (singletonModelTest == null) {
synchronized (SingletonModelTest.class) {
if (singletonModelTest == null) {
singletonModelTest = new SingletonModelTest();
}
}
}
return singletonModelTest;
}
静态内部类(推荐)
private static class SingletonInnerHandle {
private static final SingletonModelTest INSTANCE = new SingletonModelTest();
}
private SingletonModelTest() {}
public static SingletonModelTest getInstance() {
return SingletonInnerHandle.INSTANCE;
}
注:虽然静态内部类的形式创建的单例是线程安全且是懒加载的模式,但是存在反射攻击或者反序列化攻击(懒汉式、饿汉式也存在),例:
反射攻击
public static void main(String[] args) throws Exception {
SingletonModelTest singleton = SingletonModelTest.getInstance();
Constructor<SingletonModelTest> constructor = SingletonModelTest.class.getDeclaredConstructor();
constructor.setAccessible(true);
SingletonModelTest newSingleton = constructor.newInstance();
System.out.println(singleton == newSingleton);
}
结果:
反序列化攻击
public static void main(String[] args) throws Exception {
// SingletonModelTest singleton = SingletonModelTest.getInstance();
// Constructor<SingletonModelTest> constructor = SingletonModelTest.class.getDeclaredConstructor();
// constructor.setAccessible(true);
// SingletonModelTest newSingleton = constructor.newInstance();
// System.out.println(singleton == newSingleton);
// 前提,接口实现Serializable
SingletonModelTest instance = SingletonModelTest.getInstance();
byte[] serialize = SerializationUtils.serialize(instance);
SingletonModelTest newInstance = SerializationUtils.deserialize(serialize);
System.out.println(instance == newInstance);
}
结果:
枚举类(单例模式最好的实现方式,线程安全、且不存在反射攻击但不是懒加载)
public enum Singleton {
/**
* INSTANCE
*/
INSTANCE;
public void doSomething(){
}
}
例:
public enum SendMsgEnum {
INSTANCE;
private static final int core = Runtime.getRuntime().availableProcessors();
private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(core,core*2,60L, TimeUnit.SECONDS,new PriorityBlockingQueue(11,new SendMsgComparator()));
public void execute(SendMsgRunnable command){
executor.execute(command);
}
public boolean remove(SendMsgRunnable command){
return executor.remove(command);
}
}